home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Tcl-Tk 8.0 / Pre-installed version / tk8.0 / generic / tkBind.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-08-15  |  129.4 KB  |  4,534 lines  |  [TEXT/CWIE]

  1. /* 
  2.  * tkBind.c --
  3.  *
  4.  *    This file provides procedures that associate Tcl commands
  5.  *    with X events or sequences of X events.
  6.  *
  7.  * Copyright (c) 1989-1994 The Regents of the University of California.
  8.  * Copyright (c) 1994-1996 Sun Microsystems, Inc.
  9.  *
  10.  * See the file "license.terms" for information on usage and redistribution
  11.  * of this file, and for a DISCLAIMER OF ALL WARRANTIES.
  12.  *
  13.  * SCCS: @(#) tkBind.c 1.133 97/07/01 17:59:53
  14.  */
  15.  
  16. #include "tkPort.h"
  17. #include "tkInt.h"
  18.  
  19. /*
  20.  * File structure:
  21.  *
  22.  * Structure definitions and static variables.
  23.  *
  24.  * Init/Free this package.
  25.  *
  26.  * Tcl "bind" command (actually located in tkCmds.c).
  27.  * "bind" command implementation.
  28.  * "bind" implementation helpers.
  29.  *
  30.  * Tcl "event" command.
  31.  * "event" command implementation.
  32.  * "event" implementation helpers.
  33.  *
  34.  * Package-specific common helpers.
  35.  *
  36.  * Non-package-specific helpers.
  37.  */
  38.  
  39.  
  40. /*
  41.  * The following union is used to hold the detail information from an
  42.  * XEvent (including Tk's XVirtualEvent extension).
  43.  */
  44. typedef union {
  45.     KeySym    keySym;        /* KeySym that corresponds to xkey.keycode. */
  46.     int        button;        /* Button that was pressed (xbutton.button). */
  47.     Tk_Uid    name;        /* Tk_Uid of virtual event. */
  48.     ClientData    clientData; /* Used when type of Detail is unknown, and to
  49.                  * ensure that all bytes of Detail are initialized
  50.                  * when this structure is used in a hash key. */
  51. } Detail;
  52.  
  53. /*
  54.  * The structure below represents a binding table.  A binding table
  55.  * represents a domain in which event bindings may occur.  It includes
  56.  * a space of objects relative to which events occur (usually windows,
  57.  * but not always), a history of recent events in the domain, and
  58.  * a set of mappings that associate particular Tcl commands with sequences
  59.  * of events in the domain.  Multiple binding tables may exist at once,
  60.  * either because there are multiple applications open, or because there
  61.  * are multiple domains within an application with separate event
  62.  * bindings for each (for example, each canvas widget has a separate
  63.  * binding table for associating events with the items in the canvas).
  64.  *
  65.  * Note: it is probably a bad idea to reduce EVENT_BUFFER_SIZE much
  66.  * below 30.  To see this, consider a triple mouse button click while
  67.  * the Shift key is down (and auto-repeating).  There may be as many
  68.  * as 3 auto-repeat events after each mouse button press or release
  69.  * (see the first large comment block within Tk_BindEvent for more on
  70.  * this), for a total of 20 events to cover the three button presses
  71.  * and two intervening releases.  If you reduce EVENT_BUFFER_SIZE too
  72.  * much, shift multi-clicks will be lost.
  73.  * 
  74.  */
  75.  
  76. #define EVENT_BUFFER_SIZE 30
  77. typedef struct BindingTable {
  78.     XEvent eventRing[EVENT_BUFFER_SIZE];/* Circular queue of recent events
  79.                      * (higher indices are for more recent
  80.                      * events). */
  81.     Detail detailRing[EVENT_BUFFER_SIZE];/* "Detail" information (keySym,
  82.                      * button, Tk_Uid, or 0) for each
  83.                      * entry in eventRing. */
  84.     int curEvent;            /* Index in eventRing of most recent
  85.                      * event.  Newer events have higher
  86.                      * indices. */
  87.     Tcl_HashTable patternTable;        /* Used to map from an event to a
  88.                      * list of patterns that may match that
  89.                      * event.  Keys are PatternTableKey
  90.                      * structs, values are (PatSeq *). */
  91.     Tcl_HashTable objectTable;        /* Used to map from an object to a
  92.                      * list of patterns associated with
  93.                      * that object.  Keys are ClientData,
  94.                      * values are (PatSeq *). */
  95.     Tcl_Interp *interp;            /* Interpreter in which commands are
  96.                      * executed. */
  97. } BindingTable;
  98.  
  99. /*
  100.  * The following structure represents virtual event table.  A virtual event
  101.  * table provides a way to map from platform-specific physical events such
  102.  * as button clicks or key presses to virtual events such as <<Paste>>,
  103.  * <<Close>>, or <<ScrollWindow>>.
  104.  *
  105.  * A virtual event is usually never part of the event stream, but instead is
  106.  * synthesized inline by matching low-level events.  However, a virtual
  107.  * event may be generated by platform-specific code or by Tcl scripts.  In
  108.  * that case, no lookup of the virtual event will need to be done using
  109.  * this table, because the virtual event is actually in the event stream.
  110.  */
  111.  
  112. typedef struct VirtualEventTable {
  113.     Tcl_HashTable patternTable;     /* Used to map from a physical event to
  114.                      * a list of patterns that may match that
  115.                      * event.  Keys are PatternTableKey
  116.                      * structs, values are (PatSeq *). */
  117.     Tcl_HashTable nameTable;        /* Used to map a virtual event name to
  118.                      * the array of physical events that can
  119.                      * trigger it.  Keys are the Tk_Uid names
  120.                      * of the virtual events, values are
  121.                      * PhysicalsOwned structs. */
  122. } VirtualEventTable;
  123.  
  124. /*
  125.  * The following structure is used as a key in a patternTable for both 
  126.  * binding tables and a virtual event tables.
  127.  *
  128.  * In a binding table, the object field corresponds to the binding tag
  129.  * for the widget whose bindings are being accessed.
  130.  *
  131.  * In a virtual event table, the object field is always NULL.  Virtual
  132.  * events are a global definiton and are not tied to a particular
  133.  * binding tag.
  134.  *
  135.  * The same key is used for both types of pattern tables so that the 
  136.  * helper functions that traverse and match patterns will work for both
  137.  * binding tables and virtual event tables.
  138.  */
  139. typedef struct PatternTableKey {
  140.     ClientData object;        /* For binding table, identifies the binding
  141.                  * tag of the object (or class of objects)
  142.                  * relative to which the event occurred.
  143.                  * For virtual event table, always NULL. */
  144.     int type;            /* Type of event (from X). */
  145.     Detail detail;        /* Additional information, such as keysym,
  146.                  * button, Tk_Uid, or 0 if nothing
  147.                  * additional. */
  148. } PatternTableKey;
  149.  
  150. /*
  151.  * The following structure defines a pattern, which is matched against X
  152.  * events as part of the process of converting X events into Tcl commands.
  153.  */
  154.  
  155. typedef struct Pattern {
  156.     int eventType;        /* Type of X event, e.g. ButtonPress. */
  157.     int needMods;        /* Mask of modifiers that must be
  158.                  * present (0 means no modifiers are
  159.                  * required). */
  160.     Detail detail;        /* Additional information that must
  161.                  * match event.  Normally this is 0,
  162.                  * meaning no additional information
  163.                  * must match.  For KeyPress and
  164.                  * KeyRelease events, a keySym may
  165.                  * be specified to select a
  166.                  * particular keystroke (0 means any
  167.                  * keystrokes).  For button events,
  168.                  * specifies a particular button (0
  169.                  * means any buttons are OK).  For virtual
  170.                  * events, specifies the Tk_Uid of the
  171.                  * virtual event name (never 0). */
  172. } Pattern;
  173.  
  174. /*
  175.  * The following structure defines a pattern sequence, which consists of one
  176.  * or more patterns.  In order to trigger, a pattern sequence must match
  177.  * the most recent X events (first pattern to most recent event, next
  178.  * pattern to next event, and so on).  It is used as the hash value in a
  179.  * patternTable for both binding tables and virtual event tables.
  180.  *
  181.  * In a binding table, it is the sequence of physical events that make up
  182.  * a binding for an object.
  183.  * 
  184.  * In a virtual event table, it is the sequence of physical events that
  185.  * define a virtual event.
  186.  *
  187.  * The same structure is used for both types of pattern tables so that the 
  188.  * helper functions that traverse and match patterns will work for both
  189.  * binding tables and virtual event tables.
  190.  */
  191.  
  192. typedef struct PatSeq {
  193.     int numPats;        /* Number of patterns in sequence (usually
  194.                  * 1). */
  195.     TkBindEvalProc *eventProc;    /* The procedure that will be invoked on
  196.                  * the clientData when this pattern sequence
  197.                  * matches. */
  198.     TkBindFreeProc *freeProc;    /* The procedure that will be invoked to
  199.                  * release the clientData when this pattern
  200.                  * sequence is freed. */
  201.     ClientData clientData;    /* Arbitray data passed to eventProc and
  202.                  * freeProc when sequence matches. */
  203.     int flags;            /* Miscellaneous flag values; see below for
  204.                  * definitions. */
  205.     int refCount;        /* Number of times that this binding is in
  206.                  * the midst of executing.  If greater than 1,
  207.                  * then a recursive invocation is happening.
  208.                  * Only when this is zero can the binding
  209.                  * actually be freed. */
  210.     struct PatSeq *nextSeqPtr;  /* Next in list of all pattern sequences
  211.                  * that have the same initial pattern.  NULL
  212.                  * means end of list. */
  213.     Tcl_HashEntry *hPtr;    /* Pointer to hash table entry for the
  214.                  * initial pattern.  This is the head of the
  215.                  * list of which nextSeqPtr forms a part. */
  216.     struct VirtualOwners *voPtr;/* In a binding table, always NULL.  In a
  217.                  * virtual event table, identifies the array
  218.                  * of virtual events that can be triggered by
  219.                  * this event. */
  220.     struct PatSeq *nextObjPtr;  /* In a binding table, next in list of all
  221.                  * pattern sequences for the same object (NULL
  222.                  * for end of list).  Needed to implement
  223.                  * Tk_DeleteAllBindings.  In a virtual event
  224.                  * table, always NULL. */
  225.     Pattern pats[1];        /* Array of "numPats" patterns.  Only one
  226.                  * element is declared here but in actuality
  227.                  * enough space will be allocated for "numPats"
  228.                  * patterns.  To match, pats[0] must match
  229.                  * event n, pats[1] must match event n-1, etc.
  230.                  */
  231. } PatSeq;
  232.  
  233. /*
  234.  * Flag values for PatSeq structures:
  235.  *
  236.  * PAT_NEARBY        1 means that all of the events matching
  237.  *            this sequence must occur with nearby X
  238.  *            and Y mouse coordinates and close in time.
  239.  *            This is typically used to restrict multiple
  240.  *            button presses.
  241.  * MARKED_DELETED    1 means that this binding has been marked as deleted
  242.  *            and removed from the binding table, but its memory
  243.  *            could not be released because it was already queued for
  244.  *            execution.  When the binding is actually about to be
  245.  *            executed, this flag will be checked and the binding
  246.  *            skipped if set.
  247.  */
  248.  
  249. #define PAT_NEARBY        0x1
  250. #define MARKED_DELETED        0x2
  251.  
  252. /*
  253.  * Constants that define how close together two events must be
  254.  * in milliseconds or pixels to meet the PAT_NEARBY constraint:
  255.  */
  256.  
  257. #define NEARBY_PIXELS        5
  258. #define NEARBY_MS        500
  259.  
  260.  
  261. /*
  262.  * The following structure keeps track of all the virtual events that are
  263.  * associated with a particular physical event.  It is pointed to by the
  264.  * voPtr field in a PatSeq in the patternTable of a  virtual event table.
  265.  */
  266.  
  267. typedef struct VirtualOwners {
  268.     int numOwners;            /* Number of virtual events to trigger. */
  269.     Tcl_HashEntry *owners[1];        /* Array of pointers to entries in
  270.                      * nameTable.  Enough space will
  271.                      * actually be allocated for numOwners
  272.                      * hash entries. */
  273. } VirtualOwners;
  274.  
  275. /*
  276.  * The following structure is used in the nameTable of a virtual event
  277.  * table to associate a virtual event with all the physical events that can
  278.  * trigger it.
  279.  */
  280. typedef struct PhysicalsOwned {
  281.     int numOwned;            /* Number of physical events owned. */
  282.     PatSeq *patSeqs[1];            /* Array of pointers to physical event
  283.                      * patterns.  Enough space will actually
  284.                      * be allocated to hold numOwned. */
  285. } PhysicalsOwned;
  286.  
  287. /*
  288.  * One of the following structures exists for each interpreter.  This
  289.  * structure keeps track of the current display and screen in the
  290.  * interpreter, so that a script can be invoked whenever the display/screen
  291.  * changes (the script does things like point tkPriv at a display-specific
  292.  * structure).
  293.  */
  294.  
  295. typedef struct {
  296.     TkDisplay *curDispPtr;    /* Display for last binding command invoked
  297.                  * in this application. */
  298.     int curScreenIndex;        /* Index of screen for last binding command. */
  299.     int bindingDepth;        /* Number of active instances of Tk_BindEvent
  300.                  * in this application. */
  301. } ScreenInfo;
  302.  
  303. /*
  304.  * The following structure is used to keep track of all the C bindings that
  305.  * are awaiting invocation and whether the window they refer to has been
  306.  * destroyed.  If the window is destroyed, then all pending callbacks for
  307.  * that window will be cancelled.  The Tcl bindings will still all be
  308.  * invoked, however.  
  309.  */
  310.  
  311. typedef struct PendingBinding {
  312.     struct PendingBinding *nextPtr;
  313.                 /* Next in chain of pending bindings, in
  314.                  * case a recursive binding evaluation is in
  315.                  * progress. */
  316.     Tk_Window tkwin;        /* The window that the following bindings
  317.                  * depend upon. */
  318.     int deleted;        /* Set to non-zero by window cleanup code
  319.                  * if tkwin is deleted. */
  320.     PatSeq *matchArray[5];    /* Array of pending C bindings.  The actual
  321.                  * size of this depends on how many C bindings
  322.                  * matched the event passed to Tk_BindEvent.
  323.                  * THIS FIELD MUST BE THE LAST IN THE
  324.                  * STRUCTURE. */
  325. } PendingBinding;
  326.  
  327. /*
  328.  * The following structure keeps track of all the information local to
  329.  * the binding package on a per interpreter basis.
  330.  */
  331.  
  332. typedef struct BindInfo {
  333.     VirtualEventTable virtualEventTable;
  334.                 /* The virtual events that exist in this
  335.                  * interpreter. */
  336.     ScreenInfo screenInfo;    /* Keeps track of the current display and
  337.                  * screen, so it can be restored after
  338.                  * a binding has executed. */
  339.     PendingBinding *pendingList;/* The list of pending C bindings, kept in
  340.                  * case a C or Tcl binding causes the target
  341.                  * window to be deleted. */
  342. } BindInfo;
  343.     
  344. /*
  345.  * In X11R4 and earlier versions, XStringToKeysym is ridiculously
  346.  * slow.  The data structure and hash table below, along with the
  347.  * code that uses them, implement a fast mapping from strings to
  348.  * keysyms.  In X11R5 and later releases XStringToKeysym is plenty
  349.  * fast so this stuff isn't needed.  The #define REDO_KEYSYM_LOOKUP
  350.  * is normally undefined, so that XStringToKeysym gets used.  It
  351.  * can be set in the Makefile to enable the use of the hash table
  352.  * below.
  353.  */
  354.  
  355. #ifdef REDO_KEYSYM_LOOKUP
  356. typedef struct {
  357.     char *name;                /* Name of keysym. */
  358.     KeySym value;            /* Numeric identifier for keysym. */
  359. } KeySymInfo;
  360. static KeySymInfo keyArray[] = {
  361. #ifndef lint
  362. #include "ks_names.h"
  363. #endif
  364.     {(char *) NULL, 0}
  365. };
  366. static Tcl_HashTable keySymTable;    /* keyArray hashed by keysym value. */
  367. static Tcl_HashTable nameTable;        /* keyArray hashed by keysym name. */
  368. #endif /* REDO_KEYSYM_LOOKUP */
  369.  
  370. /*
  371.  * Set to non-zero when the package-wide static variables have been
  372.  * initialized.
  373.  */
  374.  
  375. static int initialized = 0;
  376.  
  377. /*
  378.  * A hash table is kept to map from the string names of event
  379.  * modifiers to information about those modifiers.  The structure
  380.  * for storing this information, and the hash table built at
  381.  * initialization time, are defined below.
  382.  */
  383.  
  384. typedef struct {
  385.     char *name;            /* Name of modifier. */
  386.     int mask;            /* Button/modifier mask value,                             * such as Button1Mask. */
  387.     int flags;            /* Various flags;  see below for
  388.                  * definitions. */
  389. } ModInfo;
  390.  
  391. /*
  392.  * Flags for ModInfo structures:
  393.  *
  394.  * DOUBLE -        Non-zero means duplicate this event,
  395.  *            e.g. for double-clicks.
  396.  * TRIPLE -        Non-zero means triplicate this event,
  397.  *            e.g. for triple-clicks.
  398.  */
  399.  
  400. #define DOUBLE        1
  401. #define TRIPLE        2
  402.  
  403. /*
  404.  * The following special modifier mask bits are defined, to indicate
  405.  * logical modifiers such as Meta and Alt that may float among the
  406.  * actual modifier bits.
  407.  */
  408.  
  409. #define META_MASK    (AnyModifier<<1)
  410. #define ALT_MASK    (AnyModifier<<2)
  411.  
  412. static ModInfo modArray[] = {
  413.     {"Control",        ControlMask,    0},
  414.     {"Shift",        ShiftMask,    0},
  415.     {"Lock",        LockMask,    0},
  416.     {"Meta",        META_MASK,    0},
  417.     {"M",        META_MASK,    0},
  418.     {"Alt",        ALT_MASK,    0},
  419.     {"B1",        Button1Mask,    0},
  420.     {"Button1",        Button1Mask,    0},
  421.     {"B2",        Button2Mask,    0},
  422.     {"Button2",        Button2Mask,    0},
  423.     {"B3",        Button3Mask,    0},
  424.     {"Button3",        Button3Mask,    0},
  425.     {"B4",        Button4Mask,    0},
  426.     {"Button4",        Button4Mask,    0},
  427.     {"B5",        Button5Mask,    0},
  428.     {"Button5",        Button5Mask,    0},
  429.     {"Mod1",        Mod1Mask,    0},
  430.     {"M1",        Mod1Mask,    0},
  431.     {"Command",        Mod1Mask,    0},
  432.     {"Mod2",        Mod2Mask,    0},
  433.     {"M2",        Mod2Mask,    0},
  434.     {"Option",        Mod2Mask,    0},
  435.     {"Mod3",        Mod3Mask,    0},
  436.     {"M3",        Mod3Mask,    0},
  437.     {"Mod4",        Mod4Mask,    0},
  438.     {"M4",        Mod4Mask,    0},
  439.     {"Mod5",        Mod5Mask,    0},
  440.     {"M5",        Mod5Mask,    0},
  441.     {"Double",        0,        DOUBLE},
  442.     {"Triple",        0,        TRIPLE},
  443.     {"Any",        0,        0},    /* Ignored: historical relic. */
  444.     {NULL,        0,        0}
  445. };
  446. static Tcl_HashTable modTable;
  447.  
  448. /*
  449.  * This module also keeps a hash table mapping from event names
  450.  * to information about those events.  The structure, an array
  451.  * to use to initialize the hash table, and the hash table are
  452.  * all defined below.
  453.  */
  454.  
  455. typedef struct {
  456.     char *name;            /* Name of event. */
  457.     int type;            /* Event type for X, such as
  458.                  * ButtonPress. */
  459.     int eventMask;        /* Mask bits (for XSelectInput)
  460.                  * for this event type. */
  461. } EventInfo;
  462.  
  463. /*
  464.  * Note:  some of the masks below are an OR-ed combination of
  465.  * several masks.  This is necessary because X doesn't report
  466.  * up events unless you also ask for down events.  Also, X
  467.  * doesn't report button state in motion events unless you've
  468.  * asked about button events.
  469.  */
  470.  
  471. static EventInfo eventArray[] = {
  472.     {"Key",        KeyPress,        KeyPressMask},
  473.     {"KeyPress",    KeyPress,        KeyPressMask},
  474.     {"KeyRelease",    KeyRelease,        KeyPressMask|KeyReleaseMask},
  475.     {"Button",        ButtonPress,        ButtonPressMask},
  476.     {"ButtonPress",    ButtonPress,        ButtonPressMask},
  477.     {"ButtonRelease",    ButtonRelease,
  478.         ButtonPressMask|ButtonReleaseMask},
  479.     {"Motion",        MotionNotify,
  480.         ButtonPressMask|PointerMotionMask},
  481.     {"Enter",        EnterNotify,        EnterWindowMask},
  482.     {"Leave",        LeaveNotify,        LeaveWindowMask},
  483.     {"FocusIn",        FocusIn,        FocusChangeMask},
  484.     {"FocusOut",    FocusOut,        FocusChangeMask},
  485.     {"Expose",        Expose,            ExposureMask},
  486.     {"Visibility",    VisibilityNotify,    VisibilityChangeMask},
  487.     {"Destroy",        DestroyNotify,        StructureNotifyMask},
  488.     {"Unmap",        UnmapNotify,        StructureNotifyMask},
  489.     {"Map",        MapNotify,        StructureNotifyMask},
  490.     {"Reparent",    ReparentNotify,        StructureNotifyMask},
  491.     {"Configure",    ConfigureNotify,    StructureNotifyMask},
  492.     {"Gravity",        GravityNotify,        StructureNotifyMask},
  493.     {"Circulate",    CirculateNotify,    StructureNotifyMask},
  494.     {"Property",    PropertyNotify,        PropertyChangeMask},
  495.     {"Colormap",    ColormapNotify,        ColormapChangeMask},
  496.     {"Activate",    ActivateNotify,        ActivateMask},
  497.     {"Deactivate",    DeactivateNotify,    ActivateMask},
  498.     {(char *) NULL,    0,            0}
  499. };
  500. static Tcl_HashTable eventTable;
  501.  
  502. /*
  503.  * The defines and table below are used to classify events into
  504.  * various groups.  The reason for this is that logically identical
  505.  * fields (e.g. "state") appear at different places in different
  506.  * types of events.  The classification masks can be used to figure
  507.  * out quickly where to extract information from events.
  508.  */
  509.  
  510. #define KEY            0x1
  511. #define BUTTON            0x2
  512. #define MOTION            0x4
  513. #define CROSSING        0x8
  514. #define FOCUS            0x10
  515. #define EXPOSE            0x20
  516. #define VISIBILITY        0x40
  517. #define CREATE            0x80
  518. #define DESTROY            0x100
  519. #define UNMAP            0x200
  520. #define MAP            0x400
  521. #define REPARENT        0x800
  522. #define CONFIG            0x1000
  523. #define GRAVITY            0x2000
  524. #define CIRC            0x4000
  525. #define PROP            0x8000
  526. #define COLORMAP        0x10000
  527. #define VIRTUAL            0x20000
  528. #define ACTIVATE        0x40000
  529.  
  530. #define KEY_BUTTON_MOTION_VIRTUAL    (KEY|BUTTON|MOTION|VIRTUAL)
  531.  
  532. static int flagArray[TK_LASTEVENT] = {
  533.    /* Not used */        0,
  534.    /* Not used */        0,
  535.    /* KeyPress */        KEY,
  536.    /* KeyRelease */        KEY,
  537.    /* ButtonPress */        BUTTON,
  538.    /* ButtonRelease */        BUTTON,
  539.    /* MotionNotify */        MOTION,
  540.    /* EnterNotify */        CROSSING,
  541.    /* LeaveNotify */        CROSSING,
  542.    /* FocusIn */        FOCUS,
  543.    /* FocusOut */        FOCUS,
  544.    /* KeymapNotify */        0,
  545.    /* Expose */            EXPOSE,
  546.    /* GraphicsExpose */        EXPOSE,
  547.    /* NoExpose */        0,
  548.    /* VisibilityNotify */    VISIBILITY,
  549.    /* CreateNotify */        CREATE,
  550.    /* DestroyNotify */        DESTROY,
  551.    /* UnmapNotify */        UNMAP,
  552.    /* MapNotify */        MAP,
  553.    /* MapRequest */        0,
  554.    /* ReparentNotify */        REPARENT,
  555.    /* ConfigureNotify */    CONFIG,
  556.    /* ConfigureRequest */    0,
  557.    /* GravityNotify */        GRAVITY,
  558.    /* ResizeRequest */        0,
  559.    /* CirculateNotify */    CIRC,
  560.    /* CirculateRequest */    0,
  561.    /* PropertyNotify */        PROP,
  562.    /* SelectionClear */        0,
  563.    /* SelectionRequest */    0,
  564.    /* SelectionNotify */    0,
  565.    /* ColormapNotify */        COLORMAP,
  566.    /* ClientMessage */        0,
  567.    /* MappingNotify */        0,
  568.    /* VirtualEvent */        VIRTUAL,
  569.    /* Activate */        ACTIVATE,        
  570.    /* Deactivate */        ACTIVATE
  571. };
  572.  
  573. /*
  574.  * The following tables are used as a two-way map between X's internal
  575.  * numeric values for fields in an XEvent and the strings used in Tcl.  The
  576.  * tables are used both when constructing an XEvent from user input and
  577.  * when providing data from an XEvent to the user.
  578.  */
  579.  
  580. static TkStateMap notifyMode[] = {
  581.     {NotifyNormal,        "NotifyNormal"},
  582.     {NotifyGrab,        "NotifyGrab"},
  583.     {NotifyUngrab,        "NotifyUngrab"},
  584.     {NotifyWhileGrabbed,    "NotifyWhileGrabbed"},
  585.     {-1, NULL}
  586. };
  587.  
  588. static TkStateMap notifyDetail[] = {
  589.     {NotifyAncestor,        "NotifyAncestor"},
  590.     {NotifyVirtual,        "NotifyVirtual"},
  591.     {NotifyInferior,        "NotifyInferior"},
  592.     {NotifyNonlinear,        "NotifyNonlinear"},
  593.     {NotifyNonlinearVirtual,    "NotifyNonlinearVirtual"},
  594.     {NotifyPointer,        "NotifyPointer"},
  595.     {NotifyPointerRoot,        "NotifyPointerRoot"},
  596.     {NotifyDetailNone,        "NotifyDetailNone"},
  597.     {-1, NULL}
  598. };
  599.  
  600. static TkStateMap circPlace[] = {
  601.     {PlaceOnTop,        "PlaceOnTop"},
  602.     {PlaceOnBottom,        "PlaceOnBottom"},
  603.     {-1, NULL}
  604. };
  605.  
  606. static TkStateMap visNotify[] = {
  607.     {VisibilityUnobscured,        "VisibilityUnobscured"},
  608.     {VisibilityPartiallyObscured,   "VisibilityPartiallyObscured"},
  609.     {VisibilityFullyObscured,        "VisibilityFullyObscured"},
  610.     {-1, NULL}
  611. };
  612.  
  613. /*
  614.  * Prototypes for local procedures defined in this file:
  615.  */
  616.  
  617. static void        ChangeScreen _ANSI_ARGS_((Tcl_Interp *interp,
  618.                 char *dispName, int screenIndex));
  619. static int        CreateVirtualEvent _ANSI_ARGS_((Tcl_Interp *interp,
  620.                 VirtualEventTable *vetPtr, char *virtString,
  621.                 char *eventString));
  622. static int        DeleteVirtualEvent _ANSI_ARGS_((Tcl_Interp *interp,
  623.                 VirtualEventTable *vetPtr, char *virtString,
  624.                 char *eventString));
  625. static void        DeleteVirtualEventTable _ANSI_ARGS_((
  626.                 VirtualEventTable *vetPtr));
  627. static void        ExpandPercents _ANSI_ARGS_((TkWindow *winPtr,
  628.                 char *before, XEvent *eventPtr, KeySym keySym,
  629.                 Tcl_DString *dsPtr));
  630. static void        FreeTclBinding _ANSI_ARGS_((ClientData clientData));
  631. static PatSeq *        FindSequence _ANSI_ARGS_((Tcl_Interp *interp,
  632.                 Tcl_HashTable *patternTablePtr, ClientData object,
  633.                 char *eventString, int create, int allowVirtual,
  634.                 unsigned long *maskPtr));
  635. static void        GetAllVirtualEvents _ANSI_ARGS_((Tcl_Interp *interp,
  636.                 VirtualEventTable *vetPtr));
  637. static char *        GetField _ANSI_ARGS_((char *p, char *copy, int size));
  638. static KeySym        GetKeySym _ANSI_ARGS_((TkDisplay *dispPtr,
  639.                 XEvent *eventPtr));
  640. static void        GetPatternString _ANSI_ARGS_((PatSeq *psPtr,
  641.                 Tcl_DString *dsPtr));
  642. static int        GetVirtualEvent _ANSI_ARGS_((Tcl_Interp *interp,
  643.                 VirtualEventTable *vetPtr, char *virtString));
  644. static Tk_Uid        GetVirtualEventUid _ANSI_ARGS_((Tcl_Interp *interp,
  645.                 char *virtString));
  646. static int        HandleEventGenerate _ANSI_ARGS_((Tcl_Interp *interp,
  647.                 Tk_Window main, int argc, char **argv));
  648. static void        InitKeymapInfo _ANSI_ARGS_((TkDisplay *dispPtr));
  649. static void        InitVirtualEventTable _ANSI_ARGS_((
  650.                 VirtualEventTable *vetPtr));
  651. static PatSeq *        MatchPatterns _ANSI_ARGS_((TkDisplay *dispPtr,
  652.                 BindingTable *bindPtr, PatSeq *psPtr,
  653.                 PatSeq *bestPtr, ClientData *objectPtr,
  654.                 PatSeq **sourcePtrPtr));
  655. static int        ParseEventDescription _ANSI_ARGS_((Tcl_Interp *interp,
  656.                 char **eventStringPtr, Pattern *patPtr,
  657.                 unsigned long *eventMaskPtr));
  658.  
  659. /*
  660.  * The following define is used as a short circuit for the callback
  661.  * procedure to evaluate a TclBinding.  The actual evaluation of the
  662.  * binding is handled inline, because special things have to be done
  663.  * with a Tcl binding before evaluation time.
  664.  */
  665.  
  666. #define EvalTclBinding    ((TkBindEvalProc *) 1)
  667.  
  668.  
  669. /*
  670.  *---------------------------------------------------------------------------
  671.  *
  672.  * TkBindInit --
  673.  *
  674.  *    This procedure is called when an application is created.  It
  675.  *    initializes all the structures used by bindings and virtual
  676.  *    events.  It must be called before any other functions in this
  677.  *    file are called.
  678.  *
  679.  * Results:
  680.  *    None.
  681.  *
  682.  * Side effects:
  683.  *    Memory allocated.
  684.  *
  685.  *---------------------------------------------------------------------------
  686.  */
  687.  
  688. void
  689. TkBindInit(mainPtr)
  690.     TkMainInfo *mainPtr;    /* The newly created application. */
  691. {
  692.     BindInfo *bindInfoPtr;
  693.  
  694.     if (sizeof(XEvent) < sizeof(XVirtualEvent)) {
  695.     panic("TkBindInit: virtual events can't be supported");
  696.     }
  697.  
  698.     /*
  699.      * Initialize the static data structures used by the binding package.
  700.      * They are only initialized once, no matter how many interps are
  701.      * created.
  702.      */
  703.  
  704.     if (!initialized) {
  705.     Tcl_HashEntry *hPtr;
  706.     ModInfo *modPtr;
  707.     EventInfo *eiPtr;
  708.     int dummy;
  709.  
  710. #ifdef REDO_KEYSYM_LOOKUP
  711.     KeySymInfo *kPtr;
  712.  
  713.     Tcl_InitHashTable(&keySymTable, TCL_STRING_KEYS);
  714.     Tcl_InitHashTable(&nameTable, TCL_ONE_WORD_KEYS);
  715.     for (kPtr = keyArray; kPtr->name != NULL; kPtr++) {
  716.         hPtr = Tcl_CreateHashEntry(&keySymTable, kPtr->name, &dummy);
  717.         Tcl_SetHashValue(hPtr, kPtr->value);
  718.         hPtr = Tcl_CreateHashEntry(&nameTable, (char *) kPtr->value,
  719.             &dummy);
  720.         Tcl_SetHashValue(hPtr, kPtr->name);
  721.     }
  722. #endif /* REDO_KEYSYM_LOOKUP */
  723.  
  724.     Tcl_InitHashTable(&modTable, TCL_STRING_KEYS);
  725.     for (modPtr = modArray; modPtr->name != NULL; modPtr++) {
  726.         hPtr = Tcl_CreateHashEntry(&modTable, modPtr->name, &dummy);
  727.         Tcl_SetHashValue(hPtr, modPtr);
  728.     }
  729.     
  730.     Tcl_InitHashTable(&eventTable, TCL_STRING_KEYS);
  731.     for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) {
  732.         hPtr = Tcl_CreateHashEntry(&eventTable, eiPtr->name, &dummy);
  733.         Tcl_SetHashValue(hPtr, eiPtr);
  734.     }
  735.     initialized = 1;
  736.     }
  737.  
  738.     mainPtr->bindingTable = Tk_CreateBindingTable(mainPtr->interp);
  739.  
  740.     bindInfoPtr = (BindInfo *) ckalloc(sizeof(BindInfo));
  741.     InitVirtualEventTable(&bindInfoPtr->virtualEventTable);
  742.     bindInfoPtr->screenInfo.curDispPtr = NULL;
  743.     bindInfoPtr->screenInfo.curScreenIndex = -1;
  744.     bindInfoPtr->screenInfo.bindingDepth = 0;
  745.     bindInfoPtr->pendingList = NULL;
  746.     mainPtr->bindInfo = (TkBindInfo) bindInfoPtr;
  747.  
  748.     TkpInitializeMenuBindings(mainPtr->interp, mainPtr->bindingTable);
  749. }
  750.  
  751. /*
  752.  *---------------------------------------------------------------------------
  753.  *
  754.  * TkBindFree --
  755.  *
  756.  *    This procedure is called when an application is deleted.  It
  757.  *    deletes all the structures used by bindings and virtual events.
  758.  *
  759.  * Results:
  760.  *    None.
  761.  *
  762.  * Side effects:
  763.  *    Memory freed.
  764.  *
  765.  *---------------------------------------------------------------------------
  766.  */
  767.  
  768. void
  769. TkBindFree(mainPtr)
  770.     TkMainInfo *mainPtr;    /* The newly created application. */
  771. {
  772.     BindInfo *bindInfoPtr;
  773.     
  774.     Tk_DeleteBindingTable(mainPtr->bindingTable);
  775.     mainPtr->bindingTable = NULL;
  776.  
  777.     bindInfoPtr = (BindInfo *) mainPtr->bindInfo;
  778.     DeleteVirtualEventTable(&bindInfoPtr->virtualEventTable);
  779.     mainPtr->bindInfo = NULL;
  780. }
  781.  
  782. /*
  783.  *--------------------------------------------------------------
  784.  *
  785.  * Tk_CreateBindingTable --
  786.  *
  787.  *    Set up a new domain in which event bindings may be created.
  788.  *
  789.  * Results:
  790.  *    The return value is a token for the new table, which must
  791.  *    be passed to procedures like Tk_CreatBinding.
  792.  *
  793.  * Side effects:
  794.  *    Memory is allocated for the new table.
  795.  *
  796.  *--------------------------------------------------------------
  797.  */
  798.  
  799. Tk_BindingTable
  800. Tk_CreateBindingTable(interp)
  801.     Tcl_Interp *interp;        /* Interpreter to associate with the binding
  802.                  * table:  commands are executed in this
  803.                  * interpreter. */
  804. {
  805.     BindingTable *bindPtr;
  806.     int i;
  807.  
  808.     /*
  809.      * Create and initialize a new binding table.
  810.      */
  811.  
  812.     bindPtr = (BindingTable *) ckalloc(sizeof(BindingTable));
  813.     for (i = 0; i < EVENT_BUFFER_SIZE; i++) {
  814.     bindPtr->eventRing[i].type = -1;
  815.     }
  816.     bindPtr->curEvent = 0;
  817.     Tcl_InitHashTable(&bindPtr->patternTable,
  818.         sizeof(PatternTableKey)/sizeof(int));
  819.     Tcl_InitHashTable(&bindPtr->objectTable, TCL_ONE_WORD_KEYS);
  820.     bindPtr->interp = interp;
  821.     return (Tk_BindingTable) bindPtr;
  822. }
  823.  
  824. /*
  825.  *--------------------------------------------------------------
  826.  *
  827.  * Tk_DeleteBindingTable --
  828.  *
  829.  *    Destroy a binding table and free up all its memory.
  830.  *    The caller should not use bindingTable again after
  831.  *    this procedure returns.
  832.  *
  833.  * Results:
  834.  *    None.
  835.  *
  836.  * Side effects:
  837.  *    Memory is freed.
  838.  *
  839.  *--------------------------------------------------------------
  840.  */
  841.  
  842. void
  843. Tk_DeleteBindingTable(bindingTable)
  844.     Tk_BindingTable bindingTable;    /* Token for the binding table to
  845.                      * destroy. */
  846. {
  847.     BindingTable *bindPtr = (BindingTable *) bindingTable;
  848.     PatSeq *psPtr, *nextPtr;
  849.     Tcl_HashEntry *hPtr;
  850.     Tcl_HashSearch search;
  851.  
  852.     /*
  853.      * Find and delete all of the patterns associated with the binding
  854.      * table.
  855.      */
  856.  
  857.     for (hPtr = Tcl_FirstHashEntry(&bindPtr->patternTable, &search);
  858.         hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
  859.     for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
  860.         psPtr != NULL; psPtr = nextPtr) {
  861.         nextPtr = psPtr->nextSeqPtr;
  862.         psPtr->flags |= MARKED_DELETED;
  863.         if (psPtr->refCount == 0) {
  864.         if (psPtr->freeProc != NULL) {
  865.             (*psPtr->freeProc)(psPtr->clientData);
  866.         }
  867.         ckfree((char *) psPtr);
  868.         }
  869.     }
  870.     }
  871.  
  872.     /*
  873.      * Clean up the rest of the information associated with the
  874.      * binding table.
  875.      */
  876.  
  877.     Tcl_DeleteHashTable(&bindPtr->patternTable);
  878.     Tcl_DeleteHashTable(&bindPtr->objectTable);
  879.     ckfree((char *) bindPtr);
  880. }
  881.  
  882. /*
  883.  *--------------------------------------------------------------
  884.  *
  885.  * Tk_CreateBinding --
  886.  *
  887.  *    Add a binding to a binding table, so that future calls to
  888.  *    Tk_BindEvent may execute the command in the binding.
  889.  *
  890.  * Results:
  891.  *    The return value is 0 if an error occurred while setting
  892.  *    up the binding.  In this case, an error message will be
  893.  *    left in interp->result.  If all went well then the return
  894.  *    value is a mask of the event types that must be made
  895.  *    available to Tk_BindEvent in order to properly detect when
  896.  *    this binding triggers.  This value can be used to determine
  897.  *    what events to select for in a window, for example.
  898.  *
  899.  * Side effects:
  900.  *    An existing binding on the same event sequence may be
  901.  *    replaced.  
  902.  *    The new binding may cause future calls to Tk_BindEvent to
  903.  *    behave differently than they did previously.
  904.  *
  905.  *--------------------------------------------------------------
  906.  */
  907.  
  908. unsigned long
  909. Tk_CreateBinding(interp, bindingTable, object, eventString, command, append)
  910.     Tcl_Interp *interp;        /* Used for error reporting. */
  911.     Tk_BindingTable bindingTable;
  912.                 /* Table in which to create binding. */
  913.     ClientData object;        /* Token for object with which binding is
  914.                  * associated. */
  915.     char *eventString;        /* String describing event sequence that
  916.                  * triggers binding. */
  917.     char *command;        /* Contains Tcl command to execute when
  918.                  * binding triggers. */
  919.     int append;            /* 0 means replace any existing binding for
  920.                  * eventString; 1 means append to that
  921.                  * binding.  If the existing binding is for a
  922.                  * callback function and not a Tcl command
  923.                  * string, the existing binding will always be
  924.                  * replaced. */
  925. {
  926.     BindingTable *bindPtr = (BindingTable *) bindingTable;
  927.     PatSeq *psPtr;
  928.     unsigned long eventMask;
  929.     char *new, *old;
  930.  
  931.     psPtr = FindSequence(interp, &bindPtr->patternTable, object, eventString,
  932.         1, 1, &eventMask);
  933.     if (psPtr == NULL) {
  934.     return 0;
  935.     }
  936.     if (psPtr->eventProc == NULL) {
  937.     int new;
  938.     Tcl_HashEntry *hPtr;
  939.     
  940.     /*
  941.      * This pattern sequence was just created.
  942.      * Link the pattern into the list associated with the object, so
  943.      * that if the object goes away, these bindings will all
  944.      * automatically be deleted.
  945.      */
  946.  
  947.     hPtr = Tcl_CreateHashEntry(&bindPtr->objectTable, (char *) object,
  948.         &new);
  949.     if (new) {
  950.         psPtr->nextObjPtr = NULL;
  951.     } else {
  952.         psPtr->nextObjPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
  953.     }
  954.     Tcl_SetHashValue(hPtr, psPtr);
  955.     } else if (psPtr->eventProc != EvalTclBinding) {
  956.     /*
  957.      * Free existing procedural binding.
  958.      */
  959.  
  960.     if (psPtr->freeProc != NULL) {
  961.         (*psPtr->freeProc)(psPtr->clientData);
  962.     }
  963.     psPtr->clientData = NULL;
  964.     append = 0;
  965.     }
  966.  
  967.     old = (char *) psPtr->clientData;
  968.     if ((append != 0) && (old != NULL)) {
  969.     int length;
  970.  
  971.     length = strlen(old) + strlen(command) + 2;
  972.     new = (char *) ckalloc((unsigned) length);
  973.     sprintf(new, "%s\n%s", old, command);
  974.     } else {
  975.     new = (char *) ckalloc((unsigned) strlen(command) + 1);
  976.     strcpy(new, command);
  977.     }
  978.     if (old != NULL) {
  979.     ckfree(old);
  980.     }
  981.     psPtr->eventProc = EvalTclBinding;
  982.     psPtr->freeProc = FreeTclBinding;
  983.     psPtr->clientData = (ClientData) new;
  984.     return eventMask;
  985. }
  986.  
  987. /*
  988.  *---------------------------------------------------------------------------
  989.  *
  990.  * TkCreateBindingProcedure --
  991.  *
  992.  *    Add a C binding to a binding table, so that future calls to
  993.  *    Tk_BindEvent may callback the procedure in the binding.
  994.  *
  995.  * Results:
  996.  *    The return value is 0 if an error occurred while setting
  997.  *    up the binding.  In this case, an error message will be
  998.  *    left in interp->result.  If all went well then the return
  999.  *    value is a mask of the event types that must be made
  1000.  *    available to Tk_BindEvent in order to properly detect when
  1001.  *    this binding triggers.  This value can be used to determine
  1002.  *    what events to select for in a window, for example.
  1003.  *
  1004.  * Side effects:
  1005.  *    Any existing binding on the same event sequence will be
  1006.  *    replaced.  
  1007.  *
  1008.  *---------------------------------------------------------------------------
  1009.  */
  1010.  
  1011. unsigned long
  1012. TkCreateBindingProcedure(interp, bindingTable, object, eventString,
  1013.     eventProc, freeProc, clientData)
  1014.     Tcl_Interp *interp;        /* Used for error reporting. */
  1015.     Tk_BindingTable bindingTable;
  1016.                 /* Table in which to create binding. */
  1017.     ClientData object;        /* Token for object with which binding is
  1018.                  * associated. */
  1019.     char *eventString;        /* String describing event sequence that
  1020.                  * triggers binding. */
  1021.     TkBindEvalProc *eventProc;    /* Procedure to invoke when binding
  1022.                  * triggers.  Must not be NULL. */
  1023.     TkBindFreeProc *freeProc;    /* Procedure to invoke when binding is
  1024.                  * freed.  May be NULL for no procedure. */
  1025.     ClientData clientData;    /* Arbitrary ClientData to pass to eventProc
  1026.                  * and freeProc. */
  1027. {
  1028.     BindingTable *bindPtr = (BindingTable *) bindingTable;
  1029.     PatSeq *psPtr;
  1030.     unsigned long eventMask;
  1031.  
  1032.     psPtr = FindSequence(interp, &bindPtr->patternTable, object, eventString,
  1033.         1, 1, &eventMask);
  1034.     if (psPtr == NULL) {
  1035.     return 0;
  1036.     }
  1037.     if (psPtr->eventProc == NULL) {
  1038.     int new;
  1039.     Tcl_HashEntry *hPtr;
  1040.     
  1041.     /*
  1042.      * This pattern sequence was just created.
  1043.      * Link the pattern into the list associated with the object, so
  1044.      * that if the object goes away, these bindings will all
  1045.      * automatically be deleted.
  1046.      */
  1047.  
  1048.     hPtr = Tcl_CreateHashEntry(&bindPtr->objectTable, (char *) object,
  1049.         &new);
  1050.     if (new) {
  1051.         psPtr->nextObjPtr = NULL;
  1052.     } else {
  1053.         psPtr->nextObjPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
  1054.     }
  1055.     Tcl_SetHashValue(hPtr, psPtr);
  1056.     } else {
  1057.  
  1058.     /*
  1059.      * Free existing callback.
  1060.      */
  1061.  
  1062.     if (psPtr->freeProc != NULL) {
  1063.         (*psPtr->freeProc)(psPtr->clientData);
  1064.     }
  1065.     }
  1066.  
  1067.     psPtr->eventProc = eventProc;
  1068.     psPtr->freeProc = freeProc;
  1069.     psPtr->clientData = clientData;
  1070.     return eventMask;
  1071. }
  1072.  
  1073. /*
  1074.  *--------------------------------------------------------------
  1075.  *
  1076.  * Tk_DeleteBinding --
  1077.  *
  1078.  *    Remove an event binding from a binding table.
  1079.  *
  1080.  * Results:
  1081.  *    The result is a standard Tcl return value.  If an error
  1082.  *    occurs then interp->result will contain an error message.
  1083.  *
  1084.  * Side effects:
  1085.  *    The binding given by object and eventString is removed
  1086.  *    from bindingTable.
  1087.  *
  1088.  *--------------------------------------------------------------
  1089.  */
  1090.  
  1091. int
  1092. Tk_DeleteBinding(interp, bindingTable, object, eventString)
  1093.     Tcl_Interp *interp;            /* Used for error reporting. */
  1094.     Tk_BindingTable bindingTable;    /* Table in which to delete binding. */
  1095.     ClientData object;            /* Token for object with which binding
  1096.                      * is associated. */
  1097.     char *eventString;            /* String describing event sequence
  1098.                      * that triggers binding. */
  1099. {
  1100.     BindingTable *bindPtr = (BindingTable *) bindingTable;
  1101.     PatSeq *psPtr, *prevPtr;
  1102.     unsigned long eventMask;
  1103.     Tcl_HashEntry *hPtr;
  1104.  
  1105.     psPtr = FindSequence(interp, &bindPtr->patternTable, object, eventString,
  1106.         0, 1, &eventMask);
  1107.     if (psPtr == NULL) {
  1108.     Tcl_ResetResult(interp);
  1109.     return TCL_OK;
  1110.     }
  1111.  
  1112.     /*
  1113.      * Unlink the binding from the list for its object, then from the
  1114.      * list for its pattern.
  1115.      */
  1116.  
  1117.     hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
  1118.     if (hPtr == NULL) {
  1119.     panic("Tk_DeleteBinding couldn't find object table entry");
  1120.     }
  1121.     prevPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
  1122.     if (prevPtr == psPtr) {
  1123.     Tcl_SetHashValue(hPtr, psPtr->nextObjPtr);
  1124.     } else {
  1125.     for ( ; ; prevPtr = prevPtr->nextObjPtr) {
  1126.         if (prevPtr == NULL) {
  1127.         panic("Tk_DeleteBinding couldn't find on object list");
  1128.         }
  1129.         if (prevPtr->nextObjPtr == psPtr) {
  1130.         prevPtr->nextObjPtr = psPtr->nextObjPtr;
  1131.         break;
  1132.         }
  1133.     }
  1134.     }
  1135.     prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
  1136.     if (prevPtr == psPtr) {
  1137.     if (psPtr->nextSeqPtr == NULL) {
  1138.         Tcl_DeleteHashEntry(psPtr->hPtr);
  1139.     } else {
  1140.         Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);
  1141.     }
  1142.     } else {
  1143.     for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
  1144.         if (prevPtr == NULL) {
  1145.         panic("Tk_DeleteBinding couldn't find on hash chain");
  1146.         }
  1147.         if (prevPtr->nextSeqPtr == psPtr) {
  1148.         prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
  1149.         break;
  1150.         }
  1151.     }
  1152.     }
  1153.  
  1154.     psPtr->flags |= MARKED_DELETED;
  1155.     if (psPtr->refCount == 0) {
  1156.     if (psPtr->freeProc != NULL) {
  1157.         (*psPtr->freeProc)(psPtr->clientData);
  1158.     }
  1159.     ckfree((char *) psPtr);
  1160.     }
  1161.     return TCL_OK;
  1162. }
  1163.  
  1164. /*
  1165.  *--------------------------------------------------------------
  1166.  *
  1167.  * Tk_GetBinding --
  1168.  *
  1169.  *    Return the command associated with a given event string.
  1170.  *
  1171.  * Results:
  1172.  *    The return value is a pointer to the command string
  1173.  *    associated with eventString for object in the domain
  1174.  *    given by bindingTable.  If there is no binding for
  1175.  *    eventString, or if eventString is improperly formed,
  1176.  *    then NULL is returned and an error message is left in
  1177.  *    interp->result.  The return value is semi-static:  it
  1178.  *    will persist until the binding is changed or deleted.
  1179.  *
  1180.  * Side effects:
  1181.  *    None.
  1182.  *
  1183.  *--------------------------------------------------------------
  1184.  */
  1185.  
  1186. char *
  1187. Tk_GetBinding(interp, bindingTable, object, eventString)
  1188.     Tcl_Interp *interp;            /* Interpreter for error reporting. */
  1189.     Tk_BindingTable bindingTable;    /* Table in which to look for
  1190.                      * binding. */
  1191.     ClientData object;            /* Token for object with which binding
  1192.                      * is associated. */
  1193.     char *eventString;            /* String describing event sequence
  1194.                      * that triggers binding. */
  1195. {
  1196.     BindingTable *bindPtr = (BindingTable *) bindingTable;
  1197.     PatSeq *psPtr;
  1198.     unsigned long eventMask;
  1199.  
  1200.     psPtr = FindSequence(interp, &bindPtr->patternTable, object, eventString,
  1201.         0, 1, &eventMask);
  1202.     if (psPtr == NULL) {
  1203.     return NULL;
  1204.     }
  1205.     if (psPtr->eventProc == EvalTclBinding) {
  1206.     return (char *) psPtr->clientData;
  1207.     }
  1208.     return "";
  1209. }
  1210.  
  1211. /*
  1212.  *--------------------------------------------------------------
  1213.  *
  1214.  * Tk_GetAllBindings --
  1215.  *
  1216.  *    Return a list of event strings for all the bindings
  1217.  *    associated with a given object.
  1218.  *
  1219.  * Results:
  1220.  *    There is no return value.  Interp->result is modified to
  1221.  *    hold a Tcl list with one entry for each binding associated
  1222.  *    with object in bindingTable.  Each entry in the list
  1223.  *    contains the event string associated with one binding.
  1224.  *
  1225.  * Side effects:
  1226.  *    None.
  1227.  *
  1228.  *--------------------------------------------------------------
  1229.  */
  1230.  
  1231. void
  1232. Tk_GetAllBindings(interp, bindingTable, object)
  1233.     Tcl_Interp *interp;            /* Interpreter returning result or
  1234.                      * error. */
  1235.     Tk_BindingTable bindingTable;    /* Table in which to look for
  1236.                      * bindings. */
  1237.     ClientData object;            /* Token for object. */
  1238.  
  1239. {
  1240.     BindingTable *bindPtr = (BindingTable *) bindingTable;
  1241.     PatSeq *psPtr;
  1242.     Tcl_HashEntry *hPtr;
  1243.     Tcl_DString ds;
  1244.  
  1245.     hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
  1246.     if (hPtr == NULL) {
  1247.     return;
  1248.     }
  1249.     Tcl_DStringInit(&ds);
  1250.     for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
  1251.         psPtr = psPtr->nextObjPtr) {
  1252.     /* 
  1253.      * For each binding, output information about each of the
  1254.      * patterns in its sequence.
  1255.      */
  1256.      
  1257.     Tcl_DStringSetLength(&ds, 0);
  1258.     GetPatternString(psPtr, &ds);
  1259.     Tcl_AppendElement(interp, Tcl_DStringValue(&ds));
  1260.     }
  1261.     Tcl_DStringFree(&ds);
  1262. }
  1263.  
  1264. /*
  1265.  *--------------------------------------------------------------
  1266.  *
  1267.  * Tk_DeleteAllBindings --
  1268.  *
  1269.  *    Remove all bindings associated with a given object in a
  1270.  *    given binding table.
  1271.  *
  1272.  * Results:
  1273.  *    All bindings associated with object are removed from
  1274.  *    bindingTable.
  1275.  *
  1276.  * Side effects:
  1277.  *    None.
  1278.  *
  1279.  *--------------------------------------------------------------
  1280.  */
  1281.  
  1282. void
  1283. Tk_DeleteAllBindings(bindingTable, object)
  1284.     Tk_BindingTable bindingTable;    /* Table in which to delete
  1285.                      * bindings. */
  1286.     ClientData object;            /* Token for object. */
  1287. {
  1288.     BindingTable *bindPtr = (BindingTable *) bindingTable;
  1289.     PatSeq *psPtr, *prevPtr;
  1290.     PatSeq *nextPtr;
  1291.     Tcl_HashEntry *hPtr;
  1292.  
  1293.     hPtr = Tcl_FindHashEntry(&bindPtr->objectTable, (char *) object);
  1294.     if (hPtr == NULL) {
  1295.     return;
  1296.     }
  1297.     for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
  1298.         psPtr = nextPtr) {
  1299.     nextPtr  = psPtr->nextObjPtr;
  1300.  
  1301.     /*
  1302.      * Be sure to remove each binding from its hash chain in the
  1303.      * pattern table.  If this is the last pattern in the chain,
  1304.      * then delete the hash entry too.
  1305.      */
  1306.  
  1307.     prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
  1308.     if (prevPtr == psPtr) {
  1309.         if (psPtr->nextSeqPtr == NULL) {
  1310.         Tcl_DeleteHashEntry(psPtr->hPtr);
  1311.         } else {
  1312.         Tcl_SetHashValue(psPtr->hPtr, psPtr->nextSeqPtr);
  1313.         }
  1314.     } else {
  1315.         for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
  1316.         if (prevPtr == NULL) {
  1317.             panic("Tk_DeleteAllBindings couldn't find on hash chain");
  1318.         }
  1319.         if (prevPtr->nextSeqPtr == psPtr) {
  1320.             prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
  1321.             break;
  1322.         }
  1323.         }
  1324.     }
  1325.     psPtr->flags |= MARKED_DELETED;
  1326.  
  1327.     if (psPtr->refCount == 0) {
  1328.         if (psPtr->freeProc != NULL) {
  1329.         (*psPtr->freeProc)(psPtr->clientData);
  1330.         }
  1331.         ckfree((char *) psPtr);
  1332.     }
  1333.     }
  1334.     Tcl_DeleteHashEntry(hPtr);
  1335. }
  1336.  
  1337. /*
  1338.  *---------------------------------------------------------------------------
  1339.  *
  1340.  * Tk_BindEvent --
  1341.  *
  1342.  *    This procedure is invoked to process an X event.  The
  1343.  *    event is added to those recorded for the binding table.
  1344.  *    Then each of the objects at *objectPtr is checked in
  1345.  *    order to see if it has a binding that matches the recent
  1346.  *    events.  If so, the most specific binding is invoked for
  1347.  *    each object.
  1348.  *
  1349.  * Results:
  1350.  *    None.
  1351.  *
  1352.  * Side effects:
  1353.  *    Depends on the command associated with the matching binding.
  1354.  *
  1355.  *    All Tcl bindings scripts for each object are accumulated before
  1356.  *    the first binding is evaluated.  If the action of a Tcl binding
  1357.  *    is to change or delete a binding, or delete the window associated
  1358.  *    with the binding, all the original Tcl binding scripts will still
  1359.  *    fire.  Contrast this with C binding procedures.  If a pending C
  1360.  *    binding (one that hasn't fired yet, but is queued to be fired for
  1361.  *    this window) is deleted, it will not be called, and if it is
  1362.  *    changed, then the new binding procedure will be called.  If the
  1363.  *    window itself is deleted, no further C binding procedures will be
  1364.  *    called for this window.  When both Tcl binding scripts and C binding
  1365.  *    procedures are interleaved, the above rules still apply. 
  1366.  *
  1367.  *---------------------------------------------------------------------------
  1368.  */
  1369.  
  1370. void
  1371. Tk_BindEvent(bindingTable, eventPtr, tkwin, numObjects, objectPtr)
  1372.     Tk_BindingTable bindingTable;    /* Table in which to look for
  1373.                      * bindings. */
  1374.     XEvent *eventPtr;            /* What actually happened. */
  1375.     Tk_Window tkwin;            /* Window on display where event
  1376.                      * occurred (needed in order to
  1377.                      * locate display information). */
  1378.     int numObjects;            /* Number of objects at *objectPtr. */
  1379.     ClientData *objectPtr;        /* Array of one or more objects
  1380.                      * to check for a matching binding. */
  1381. {
  1382.     BindingTable *bindPtr;
  1383.     TkDisplay *dispPtr;
  1384.     BindInfo *bindInfoPtr;
  1385.     TkDisplay *oldDispPtr;
  1386.     ScreenInfo *screenPtr;
  1387.     XEvent *ringPtr;
  1388.     PatSeq *vMatchDetailList, *vMatchNoDetailList;
  1389.     int flags, oldScreen, i, deferModal;
  1390.     unsigned int matchCount, matchSpace;
  1391.     Tcl_Interp *interp;
  1392.     Tcl_DString scripts, savedResult;
  1393.     Detail detail;
  1394.     char *p, *end;
  1395.     PendingBinding *pendingPtr;
  1396.     PendingBinding staticPending;
  1397.     TkWindow *winPtr = (TkWindow *)tkwin;
  1398.     PatternTableKey key;
  1399.  
  1400.     /*
  1401.      * Ignore events on windows that don't have names: these are windows
  1402.      * like wrapper windows that shouldn't be visible to the
  1403.      * application.
  1404.      */
  1405.  
  1406.     if (winPtr->pathName == NULL) {
  1407.     return;
  1408.     }
  1409.  
  1410.     /*
  1411.      * Ignore the event completely if it is an Enter, Leave, FocusIn,
  1412.      * or FocusOut event with detail NotifyInferior.  The reason for
  1413.      * ignoring these events is that we don't want transitions between
  1414.      * a window and its children to visible to bindings on the parent:
  1415.      * this would cause problems for mega-widgets, since the internal
  1416.      * structure of a mega-widget isn't supposed to be visible to
  1417.      * people watching the parent.
  1418.      */
  1419.  
  1420.     if ((eventPtr->type == EnterNotify)  || (eventPtr->type == LeaveNotify)) {
  1421.     if (eventPtr->xcrossing.detail == NotifyInferior) {
  1422.         return;
  1423.     }
  1424.     }
  1425.     if ((eventPtr->type == FocusIn)  || (eventPtr->type == FocusOut)) {
  1426.     if (eventPtr->xfocus.detail == NotifyInferior) {
  1427.         return;
  1428.     }
  1429.     }
  1430.  
  1431.     bindPtr = (BindingTable *) bindingTable;
  1432.     dispPtr = ((TkWindow *) tkwin)->dispPtr;
  1433.     bindInfoPtr = (BindInfo *) winPtr->mainPtr->bindInfo;
  1434.  
  1435.     /*
  1436.      * Add the new event to the ring of saved events for the
  1437.      * binding table.  Two tricky points:
  1438.      *
  1439.      * 1. Combine consecutive MotionNotify events.  Do this by putting
  1440.      *    the new event *on top* of the previous event.
  1441.      * 2. If a modifier key is held down, it auto-repeats to generate
  1442.      *    continuous KeyPress and KeyRelease events.  These can flush
  1443.      *    the event ring so that valuable information is lost (such
  1444.      *    as repeated button clicks).  To handle this, check for the
  1445.      *    special case of a modifier KeyPress arriving when the previous
  1446.      *    two events are a KeyRelease and KeyPress of the same key.
  1447.      *    If this happens, mark the most recent event (the KeyRelease)
  1448.      *    invalid and put the new event on top of the event before that
  1449.      *    (the KeyPress).
  1450.      */
  1451.  
  1452.     if ((eventPtr->type == MotionNotify)
  1453.         && (bindPtr->eventRing[bindPtr->curEvent].type == MotionNotify)) {
  1454.     /*
  1455.      * Don't advance the ring pointer.
  1456.      */
  1457.     } else if (eventPtr->type == KeyPress) {
  1458.     int i;
  1459.     for (i = 0; ; i++) {
  1460.         if (i >= dispPtr->numModKeyCodes) {
  1461.         goto advanceRingPointer;
  1462.         }
  1463.         if (dispPtr->modKeyCodes[i] == eventPtr->xkey.keycode) {
  1464.         break;
  1465.         }
  1466.     }
  1467.     ringPtr = &bindPtr->eventRing[bindPtr->curEvent];
  1468.     if ((ringPtr->type != KeyRelease)
  1469.         || (ringPtr->xkey.keycode != eventPtr->xkey.keycode)) {
  1470.         goto advanceRingPointer;
  1471.     }
  1472.     if (bindPtr->curEvent <= 0) {
  1473.         i = EVENT_BUFFER_SIZE - 1;
  1474.     } else {
  1475.         i = bindPtr->curEvent - 1;
  1476.     }
  1477.     ringPtr = &bindPtr->eventRing[i];
  1478.     if ((ringPtr->type != KeyPress)
  1479.         || (ringPtr->xkey.keycode != eventPtr->xkey.keycode)) {
  1480.         goto advanceRingPointer;
  1481.     }
  1482.     bindPtr->eventRing[bindPtr->curEvent].type = -1;
  1483.     bindPtr->curEvent = i;
  1484.     } else {
  1485.     advanceRingPointer:
  1486.     bindPtr->curEvent++;
  1487.     if (bindPtr->curEvent >= EVENT_BUFFER_SIZE) {
  1488.         bindPtr->curEvent = 0;
  1489.     }
  1490.     }
  1491.     ringPtr = &bindPtr->eventRing[bindPtr->curEvent];
  1492.     memcpy((VOID *) ringPtr, (VOID *) eventPtr, sizeof(XEvent));
  1493.     detail.clientData = 0;
  1494.     flags = flagArray[ringPtr->type];
  1495.     if (flags & KEY) {
  1496.     detail.keySym = GetKeySym(dispPtr, ringPtr);
  1497.     if (detail.keySym == NoSymbol) {
  1498.         detail.keySym = 0;
  1499.     }
  1500.     } else if (flags & BUTTON) {
  1501.     detail.button = ringPtr->xbutton.button;
  1502.     } else if (flags & VIRTUAL) {
  1503.     detail.name = ((XVirtualEvent *) ringPtr)->name;
  1504.     }
  1505.     bindPtr->detailRing[bindPtr->curEvent] = detail;
  1506.  
  1507.     /*
  1508.      * Find out if there are any virtual events that correspond to this
  1509.      * physical event (or sequence of physical events).
  1510.      */
  1511.  
  1512.     vMatchDetailList = NULL;
  1513.     vMatchNoDetailList = NULL;
  1514.     memset(&key, 0, sizeof(key));
  1515.  
  1516.     if (ringPtr->type != VirtualEvent) {
  1517.     Tcl_HashTable *veptPtr;
  1518.     Tcl_HashEntry *hPtr;
  1519.  
  1520.     veptPtr = &bindInfoPtr->virtualEventTable.patternTable;
  1521.  
  1522.         key.object  = NULL;
  1523.     key.type    = ringPtr->type;
  1524.     key.detail  = detail;
  1525.  
  1526.     hPtr = Tcl_FindHashEntry(veptPtr, (char *) &key);
  1527.     if (hPtr != NULL) {
  1528.         vMatchDetailList = (PatSeq *) Tcl_GetHashValue(hPtr);
  1529.     }
  1530.  
  1531.     if (key.detail.clientData != 0) {
  1532.         key.detail.clientData = 0;
  1533.         hPtr = Tcl_FindHashEntry(veptPtr, (char *) &key);
  1534.         if (hPtr != NULL) {
  1535.             vMatchNoDetailList = (PatSeq *) Tcl_GetHashValue(hPtr);
  1536.         }
  1537.     }
  1538.     }
  1539.  
  1540.     /*
  1541.      * Loop over all the binding tags, finding the binding script or
  1542.      * callback for each one.  Append all of the binding scripts, with
  1543.      * %-sequences expanded, to "scripts", with null characters separating
  1544.      * the scripts for each object.  Append all the callbacks to the array
  1545.      * of pending callbacks.  
  1546.      */
  1547.            
  1548.     pendingPtr = &staticPending;
  1549.     matchCount = 0;
  1550.     matchSpace = sizeof(staticPending.matchArray) / sizeof(PatSeq *);
  1551.     Tcl_DStringInit(&scripts);
  1552.  
  1553.     for ( ; numObjects > 0; numObjects--, objectPtr++) {
  1554.     PatSeq *matchPtr, *sourcePtr;
  1555.     Tcl_HashEntry *hPtr;
  1556.  
  1557.     matchPtr = NULL;
  1558.     sourcePtr = NULL;
  1559.  
  1560.     /*
  1561.      * Match the new event against those recorded in the pattern table,
  1562.      * saving the longest matching pattern.  For events with details
  1563.      * (button and key events), look for a binding for the specific
  1564.      * key or button.  First see if the event matches a physical event
  1565.      * that the object is interested in, then look for a virtual event.
  1566.      */
  1567.  
  1568.     key.object = *objectPtr;
  1569.     key.type = ringPtr->type;
  1570.     key.detail = detail;
  1571.     hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
  1572.     if (hPtr != NULL) {
  1573.         matchPtr = MatchPatterns(dispPtr, bindPtr, 
  1574.             (PatSeq *) Tcl_GetHashValue(hPtr), matchPtr, NULL,
  1575.             &sourcePtr);
  1576.     }
  1577.  
  1578.     if (vMatchDetailList != NULL) {
  1579.         matchPtr = MatchPatterns(dispPtr, bindPtr, vMatchDetailList,
  1580.             matchPtr, objectPtr, &sourcePtr);
  1581.     }
  1582.  
  1583.     /*
  1584.      * If no match was found, look for a binding for all keys or buttons
  1585.      * (detail of 0).  Again, first match on a virtual event.
  1586.      */
  1587.  
  1588.     if ((detail.clientData != 0) && (matchPtr == NULL)) {
  1589.         key.detail.clientData = 0;
  1590.         hPtr = Tcl_FindHashEntry(&bindPtr->patternTable, (char *) &key);
  1591.         if (hPtr != NULL) {
  1592.         matchPtr = MatchPatterns(dispPtr, bindPtr,
  1593.             (PatSeq *) Tcl_GetHashValue(hPtr), matchPtr, NULL,
  1594.             &sourcePtr);
  1595.         }
  1596.  
  1597.         if (vMatchNoDetailList != NULL) {
  1598.             matchPtr = MatchPatterns(dispPtr, bindPtr, vMatchNoDetailList,
  1599.             matchPtr, objectPtr, &sourcePtr);
  1600.         }
  1601.  
  1602.     }
  1603.     
  1604.     if (matchPtr != NULL) {
  1605.         if (sourcePtr->eventProc == NULL) {
  1606.         panic("Tk_BindEvent: missing command");
  1607.         }
  1608.         if (sourcePtr->eventProc == EvalTclBinding) {
  1609.         ExpandPercents(winPtr, (char *) sourcePtr->clientData,
  1610.             eventPtr, detail.keySym, &scripts);
  1611.         } else {
  1612.         if (matchCount >= matchSpace) {
  1613.             PendingBinding *new;
  1614.             unsigned int oldSize, newSize;
  1615.             
  1616.             oldSize = sizeof(staticPending)
  1617.             - sizeof(staticPending.matchArray)
  1618.             + matchSpace * sizeof(PatSeq*);
  1619.             matchSpace *= 2;
  1620.             newSize = sizeof(staticPending)
  1621.             - sizeof(staticPending.matchArray)
  1622.             + matchSpace * sizeof(PatSeq*);
  1623.             new = (PendingBinding *) ckalloc(newSize);
  1624.             memcpy((VOID *) new, (VOID *) pendingPtr, oldSize);
  1625.             if (pendingPtr != &staticPending) {
  1626.             ckfree((char *) pendingPtr);
  1627.             }
  1628.             pendingPtr = new;
  1629.         }
  1630.         sourcePtr->refCount++;
  1631.         pendingPtr->matchArray[matchCount] = sourcePtr;
  1632.         matchCount++;
  1633.         }
  1634.         /*
  1635.          * A "" is added to the scripts string to separate the
  1636.          * various scripts that should be invoked.
  1637.          */
  1638.  
  1639.         Tcl_DStringAppend(&scripts, "", 1);
  1640.     }
  1641.     }
  1642.     if (Tcl_DStringLength(&scripts) == 0) {
  1643.     return;
  1644.     }
  1645.  
  1646.     /*
  1647.      * Now go back through and evaluate the binding for each object,
  1648.      * in order, dealing with "break" and "continue" exceptions
  1649.      * appropriately.
  1650.      *
  1651.      * There are two tricks here:
  1652.      * 1. Bindings can be invoked from in the middle of Tcl commands,
  1653.      *    where interp->result is significant (for example, a widget
  1654.      *    might be deleted because of an error in creating it, so the
  1655.      *    result contains an error message that is eventually going to
  1656.      *    be returned by the creating command).  To preserve the result,
  1657.      *    we save it in a dynamic string.
  1658.      * 2. The binding's action can potentially delete the binding,
  1659.      *    so bindPtr may not point to anything valid once the action
  1660.      *    completes.  Thus we have to save bindPtr->interp in a
  1661.      *    local variable in order to restore the result.
  1662.      */
  1663.  
  1664.     interp = bindPtr->interp;
  1665.     Tcl_DStringInit(&savedResult);
  1666.  
  1667.     /*
  1668.      * Save information about the current screen, then invoke a script
  1669.      * if the screen has changed.
  1670.      */
  1671.  
  1672.     Tcl_DStringGetResult(interp, &savedResult);
  1673.     screenPtr = &bindInfoPtr->screenInfo;
  1674.     oldDispPtr = screenPtr->curDispPtr;
  1675.     oldScreen = screenPtr->curScreenIndex;
  1676.     if ((dispPtr != screenPtr->curDispPtr)
  1677.         || (Tk_ScreenNumber(tkwin) != screenPtr->curScreenIndex)) {
  1678.     screenPtr->curDispPtr = dispPtr;
  1679.     screenPtr->curScreenIndex = Tk_ScreenNumber(tkwin);
  1680.     ChangeScreen(interp, dispPtr->name, screenPtr->curScreenIndex);
  1681.     }
  1682.  
  1683.     if (matchCount > 0) {
  1684.     pendingPtr->nextPtr = bindInfoPtr->pendingList;
  1685.     pendingPtr->tkwin = tkwin;
  1686.     pendingPtr->deleted = 0;
  1687.     bindInfoPtr->pendingList = pendingPtr;
  1688.     }
  1689.     
  1690.     /*
  1691.      * Save the current value of the TK_DEFER_MODAL flag so we can
  1692.      * restore it at the end of the loop.  Clear the flag so we can
  1693.      * detect any recursive requests for a modal loop.
  1694.      */
  1695.  
  1696.     flags = winPtr->flags;
  1697.     winPtr->flags &= ~TK_DEFER_MODAL;
  1698.  
  1699.     p = Tcl_DStringValue(&scripts);
  1700.     end = p + Tcl_DStringLength(&scripts);
  1701.     i = 0;
  1702.  
  1703.     while (p < end) {
  1704.     int code;
  1705.     
  1706.     screenPtr->bindingDepth++;
  1707.     Tcl_AllowExceptions(interp);
  1708.  
  1709.     if (*p == '\0') {
  1710.         PatSeq *psPtr;
  1711.         
  1712.         psPtr = pendingPtr->matchArray[i];
  1713.         i++;
  1714.         code = TCL_OK;
  1715.         if ((pendingPtr->deleted == 0)
  1716.             && ((psPtr->flags & MARKED_DELETED) == 0)) {
  1717.         code = (*psPtr->eventProc)(psPtr->clientData, interp, eventPtr,
  1718.             tkwin, detail.keySym);
  1719.         }
  1720.         psPtr->refCount--;
  1721.         if ((psPtr->refCount == 0) && (psPtr->flags & MARKED_DELETED)) {
  1722.         if (psPtr->freeProc != NULL) {
  1723.             (*psPtr->freeProc)(psPtr->clientData);
  1724.         }
  1725.         ckfree((char *) psPtr);
  1726.         }
  1727.     } else {
  1728.         code = Tcl_GlobalEval(interp, p);
  1729.         p += strlen(p);
  1730.     }
  1731.     p++;
  1732.     screenPtr->bindingDepth--;
  1733.     if (code != TCL_OK) {
  1734.         if (code == TCL_CONTINUE) {
  1735.         /*
  1736.          * Do nothing:  just go on to the next command.
  1737.          */
  1738.         } else if (code == TCL_BREAK) {
  1739.         break;
  1740.         } else {
  1741.         Tcl_AddErrorInfo(interp, "\n    (command bound to event)");
  1742.         Tcl_BackgroundError(interp);
  1743.         break;
  1744.         }
  1745.     }
  1746.     }
  1747.  
  1748.     if (matchCount > 0 && !pendingPtr->deleted) {
  1749.     /*
  1750.      * Restore the original modal flag value and invoke the modal loop
  1751.      * if needed.
  1752.      */
  1753.  
  1754.     deferModal = winPtr->flags & TK_DEFER_MODAL;
  1755.     winPtr->flags = (winPtr->flags & (unsigned int) ~TK_DEFER_MODAL) 
  1756.         | (flags & TK_DEFER_MODAL);
  1757.     if (deferModal) {
  1758.         (*winPtr->classProcsPtr->modalProc)(tkwin, eventPtr);
  1759.     }
  1760.     }
  1761.  
  1762.     if ((screenPtr->bindingDepth != 0) &&
  1763.             ((oldDispPtr != screenPtr->curDispPtr)
  1764.                     || (oldScreen != screenPtr->curScreenIndex))) {
  1765.  
  1766.     /*
  1767.      * Some other binding script is currently executing, but its
  1768.      * screen is no longer current.  Change the current display
  1769.      * back again.
  1770.      */
  1771.  
  1772.     screenPtr->curDispPtr = oldDispPtr;
  1773.     screenPtr->curScreenIndex = oldScreen;
  1774.     ChangeScreen(interp, oldDispPtr->name, oldScreen);
  1775.     }
  1776.     Tcl_DStringResult(interp, &savedResult);
  1777.     Tcl_DStringFree(&scripts);
  1778.  
  1779.     if (matchCount > 0) {
  1780.     PendingBinding **curPtrPtr;
  1781.  
  1782.     for (curPtrPtr = &bindInfoPtr->pendingList; ; ) {
  1783.         if (*curPtrPtr == pendingPtr) {
  1784.         *curPtrPtr = pendingPtr->nextPtr;
  1785.         break;
  1786.         }
  1787.         curPtrPtr = &(*curPtrPtr)->nextPtr;
  1788.     }
  1789.     if (pendingPtr != &staticPending) {
  1790.         ckfree((char *) pendingPtr);
  1791.     }
  1792.     }
  1793. }
  1794.  
  1795. /*
  1796.  *---------------------------------------------------------------------------
  1797.  *
  1798.  * TkBindDeadWindow --
  1799.  *
  1800.  *    This procedure is invoked when it is determined that a window is
  1801.  *    dead.  It cleans up bind-related information about the window
  1802.  *
  1803.  * Results:
  1804.  *    None.
  1805.  *
  1806.  * Side effects:
  1807.  *    Any pending C bindings for this window are cancelled.
  1808.  *
  1809.  *---------------------------------------------------------------------------
  1810.  */
  1811.  
  1812. void
  1813. TkBindDeadWindow(winPtr)
  1814.     TkWindow *winPtr;        /* The window that is being deleted. */
  1815. {
  1816.     BindInfo *bindInfoPtr;
  1817.     PendingBinding *curPtr;
  1818.  
  1819.     bindInfoPtr = (BindInfo *) winPtr->mainPtr->bindInfo;
  1820.     curPtr = bindInfoPtr->pendingList;
  1821.     while (curPtr != NULL) {
  1822.     if (curPtr->tkwin == (Tk_Window) winPtr) {
  1823.         curPtr->deleted = 1;
  1824.     }
  1825.     curPtr = curPtr->nextPtr;
  1826.     }
  1827. }
  1828.  
  1829. /*
  1830.  *----------------------------------------------------------------------
  1831.  *
  1832.  * MatchPatterns --
  1833.  *
  1834.  *      Given a list of pattern sequences and a list of recent events,
  1835.  *      return the pattern sequence that best matches the event list,
  1836.  *    if there is one.
  1837.  *
  1838.  *    This procedure is used in two different ways.  In the simplest
  1839.  *    use, "object" is NULL and psPtr is a list of pattern sequences,
  1840.  *    each of which corresponds to a binding.  In this case, the
  1841.  *    procedure finds the pattern sequences that match the event list
  1842.  *    and returns the most specific of those, if there is more than one.
  1843.  *
  1844.  *    In the second case, psPtr is a list of pattern sequences, each
  1845.  *    of which corresponds to a definition for a virtual binding.
  1846.  *    In order for one of these sequences to "match", it must match
  1847.  *    the events (as above) but in addition there must be a binding
  1848.  *    for its associated virtual event on the current object.  The
  1849.  *    "object" argument indicates which object the binding must be for.
  1850.  *
  1851.  * Results:
  1852.  *      The return value is NULL if bestPtr is NULL and no pattern matches
  1853.  *    the recent events from bindPtr.  Otherwise the return value is
  1854.  *    the most specific pattern sequence among bestPtr and all those
  1855.  *    at psPtr that match the event list and object.  If a pattern
  1856.  *    sequence other than bestPtr is returned, then *bestCommandPtr
  1857.  *    is filled in with a pointer to the command from the best sequence.
  1858.  *
  1859.  * Side effects:
  1860.  *      None.
  1861.  *
  1862.  *----------------------------------------------------------------------
  1863.  */
  1864. static PatSeq *
  1865. MatchPatterns(dispPtr, bindPtr, psPtr, bestPtr, objectPtr, sourcePtrPtr)
  1866.     TkDisplay *dispPtr;        /* Display from which the event came. */
  1867.     BindingTable *bindPtr;    /* Information about binding table, such as
  1868.                  * ring of recent events. */
  1869.     PatSeq *psPtr;        /* List of pattern sequences. */
  1870.     PatSeq *bestPtr;         /* The best match seen so far, from a
  1871.                  * previous call to this procedure.  NULL
  1872.                  * means no prior best match. */
  1873.     ClientData *objectPtr;    /* If NULL, the sequences at psPtr
  1874.                  * correspond to "normal" bindings.  If
  1875.                  * non-NULL, the sequences at psPtr correspond
  1876.                  * to virtual bindings; in order to match each
  1877.                  * sequence must correspond to a virtual
  1878.                  * binding for which a binding exists for
  1879.                  * object in bindPtr. */
  1880.     PatSeq **sourcePtrPtr;    /* Filled with the pattern sequence that
  1881.                  * contains the eventProc and clientData
  1882.                  * associated with the best match.  If this
  1883.                  * differs from the return value, it is the
  1884.                  * virtual event that most closely matched the
  1885.                  * return value (a physical event).  Not
  1886.                  * modified unless a result other than bestPtr
  1887.                  * is returned. */
  1888. {
  1889.     PatSeq *matchPtr, *bestSourcePtr, *sourcePtr;
  1890.  
  1891.     bestSourcePtr = *sourcePtrPtr;
  1892.  
  1893.     /*
  1894.      * Iterate over all the pattern sequences.
  1895.      */
  1896.  
  1897.     for ( ; psPtr != NULL; psPtr = psPtr->nextSeqPtr) {
  1898.     XEvent *eventPtr;
  1899.     Pattern *patPtr;
  1900.     Window window;
  1901.     Detail *detailPtr;
  1902.     int patCount, ringCount, flags, state;
  1903.     int modMask;
  1904.  
  1905.     /*
  1906.      * Iterate over all the patterns in a sequence to be
  1907.      * sure that they all match.
  1908.      */
  1909.  
  1910.     eventPtr = &bindPtr->eventRing[bindPtr->curEvent];
  1911.     detailPtr = &bindPtr->detailRing[bindPtr->curEvent];
  1912.     window = eventPtr->xany.window;
  1913.     patPtr = psPtr->pats;
  1914.     patCount = psPtr->numPats;
  1915.     ringCount = EVENT_BUFFER_SIZE;
  1916.     while (patCount > 0) {
  1917.         if (ringCount <= 0) {
  1918.         goto nextSequence;
  1919.         }
  1920.         if (eventPtr->xany.type != patPtr->eventType) {
  1921.         /*
  1922.          * Most of the event types are considered superfluous
  1923.          * in that they are ignored if they occur in the middle
  1924.          * of a pattern sequence and have mismatching types.  The
  1925.          * only ones that cannot be ignored are ButtonPress and
  1926.          * ButtonRelease events (if the next event in the pattern
  1927.          * is a KeyPress or KeyRelease) and KeyPress and KeyRelease
  1928.          * events (if the next pattern event is a ButtonPress or
  1929.          * ButtonRelease).  Here are some tricky cases to consider:
  1930.          * 1. Double-Button or Double-Key events.
  1931.          * 2. Double-ButtonRelease or Double-KeyRelease events.
  1932.          * 3. The arrival of various events like Enter and Leave
  1933.          *    and FocusIn and GraphicsExpose between two button
  1934.          *    presses or key presses.
  1935.          * 4. Modifier keys like Shift and Control shouldn't
  1936.          *    generate conflicts with button events.
  1937.          */
  1938.  
  1939.         if ((patPtr->eventType == KeyPress)
  1940.             || (patPtr->eventType == KeyRelease)) {
  1941.             if ((eventPtr->xany.type == ButtonPress)
  1942.                 || (eventPtr->xany.type == ButtonRelease)) {
  1943.             goto nextSequence;
  1944.             }
  1945.         } else if ((patPtr->eventType == ButtonPress)
  1946.             || (patPtr->eventType == ButtonRelease)) {
  1947.             if ((eventPtr->xany.type == KeyPress)
  1948.                 || (eventPtr->xany.type == KeyRelease)) {
  1949.             int i;
  1950.  
  1951.             /*
  1952.              * Ignore key events if they are modifier keys.
  1953.              */
  1954.  
  1955.             for (i = 0; i < dispPtr->numModKeyCodes; i++) {
  1956.                 if (dispPtr->modKeyCodes[i]
  1957.                     == eventPtr->xkey.keycode) {
  1958.                 /*
  1959.                  * This key is a modifier key, so ignore it.
  1960.                  */
  1961.                 goto nextEvent;
  1962.                 }
  1963.             }
  1964.             goto nextSequence;
  1965.             }
  1966.         }
  1967.         goto nextEvent;
  1968.         }
  1969.         if (eventPtr->xany.window != window) {
  1970.         goto nextSequence;
  1971.         }
  1972.  
  1973.         /*
  1974.          * Note: it's important for the keysym check to go before
  1975.          * the modifier check, so we can ignore unwanted modifier
  1976.          * keys before choking on the modifier check.
  1977.          */
  1978.  
  1979.         if ((patPtr->detail.clientData != 0)
  1980.             && (patPtr->detail.clientData != detailPtr->clientData)) {
  1981.         /*
  1982.          * The detail appears not to match.  However, if the event
  1983.          * is a KeyPress for a modifier key then just ignore the
  1984.          * event.  Otherwise event sequences like "aD" never match
  1985.          * because the shift key goes down between the "a" and the
  1986.          * "D".
  1987.          */
  1988.  
  1989.         if (eventPtr->xany.type == KeyPress) {
  1990.             int i;
  1991.  
  1992.             for (i = 0; i < dispPtr->numModKeyCodes; i++) {
  1993.             if (dispPtr->modKeyCodes[i] == eventPtr->xkey.keycode) {
  1994.                 goto nextEvent;
  1995.             }
  1996.             }
  1997.         }
  1998.         goto nextSequence;
  1999.         }
  2000.         flags = flagArray[eventPtr->type];
  2001.         if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
  2002.         state = eventPtr->xkey.state;
  2003.         } else if (flags & CROSSING) {
  2004.         state = eventPtr->xcrossing.state;
  2005.         } else {
  2006.         state = 0;
  2007.         }
  2008.         if (patPtr->needMods != 0) {
  2009.         modMask = patPtr->needMods;
  2010.         if ((modMask & META_MASK) && (dispPtr->metaModMask != 0)) {
  2011.             modMask = (modMask & ~META_MASK) | dispPtr->metaModMask;
  2012.         }
  2013.         if ((modMask & ALT_MASK) && (dispPtr->altModMask != 0)) {
  2014.             modMask = (modMask & ~ALT_MASK) | dispPtr->altModMask;
  2015.         }
  2016.         if ((state & modMask) != modMask) {
  2017.             goto nextSequence;
  2018.         }
  2019.         }
  2020.         if (psPtr->flags & PAT_NEARBY) {
  2021.         XEvent *firstPtr;
  2022.         int timeDiff;
  2023.  
  2024.         firstPtr = &bindPtr->eventRing[bindPtr->curEvent];
  2025.         timeDiff = (Time) firstPtr->xkey.time - eventPtr->xkey.time;
  2026.         if ((firstPtr->xkey.x_root
  2027.                 < (eventPtr->xkey.x_root - NEARBY_PIXELS))
  2028.             || (firstPtr->xkey.x_root
  2029.                 > (eventPtr->xkey.x_root + NEARBY_PIXELS))
  2030.             || (firstPtr->xkey.y_root
  2031.                 < (eventPtr->xkey.y_root - NEARBY_PIXELS))
  2032.             || (firstPtr->xkey.y_root
  2033.                 > (eventPtr->xkey.y_root + NEARBY_PIXELS))
  2034.             || (timeDiff > NEARBY_MS)) {
  2035.             goto nextSequence;
  2036.         }
  2037.         }
  2038.         patPtr++;
  2039.         patCount--;
  2040.         nextEvent:
  2041.         if (eventPtr == bindPtr->eventRing) {
  2042.         eventPtr = &bindPtr->eventRing[EVENT_BUFFER_SIZE-1];
  2043.         detailPtr = &bindPtr->detailRing[EVENT_BUFFER_SIZE-1];
  2044.         } else {
  2045.         eventPtr--;
  2046.         detailPtr--;
  2047.         }
  2048.         ringCount--;
  2049.     }
  2050.  
  2051.     matchPtr = psPtr;
  2052.     sourcePtr = psPtr;
  2053.  
  2054.     if (objectPtr != NULL) {
  2055.         int iVirt;
  2056.         VirtualOwners *voPtr;
  2057.         PatternTableKey key;
  2058.  
  2059.         /*
  2060.          * The sequence matches the physical constraints.
  2061.          * Is this object interested in any of the virtual events
  2062.          * that correspond to this sequence?  
  2063.          */
  2064.  
  2065.         voPtr = psPtr->voPtr;
  2066.  
  2067.         memset(&key, 0, sizeof(key));
  2068.         key.object = *objectPtr;
  2069.         key.type = VirtualEvent;
  2070.         key.detail.clientData = 0;
  2071.  
  2072.         for (iVirt = 0; iVirt < voPtr->numOwners; iVirt++) {
  2073.             Tcl_HashEntry *hPtr = voPtr->owners[iVirt];
  2074.  
  2075.             key.detail.name = (Tk_Uid) Tcl_GetHashKey(hPtr->tablePtr,
  2076.             hPtr);
  2077.         hPtr = Tcl_FindHashEntry(&bindPtr->patternTable,
  2078.             (char *) &key);
  2079.         if (hPtr != NULL) {
  2080.  
  2081.             /*
  2082.              * This tag is interested in this virtual event and its
  2083.              * corresponding physical event is a good match with the
  2084.              * virtual event's definition.
  2085.              */
  2086.  
  2087.             PatSeq *virtMatchPtr;
  2088.  
  2089.             virtMatchPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
  2090.             if ((virtMatchPtr->numPats != 1)
  2091.                 || (virtMatchPtr->nextSeqPtr != NULL)) {
  2092.             panic("MatchPattern: badly constructed virtual event");
  2093.             }
  2094.             sourcePtr = virtMatchPtr;
  2095.             goto match;
  2096.         }
  2097.         }
  2098.  
  2099.         /*
  2100.          * The physical event matches a virtual event's definition, but
  2101.          * the tag isn't interested in it.
  2102.          */
  2103.         goto nextSequence;
  2104.     }
  2105.     match:
  2106.  
  2107.     /*
  2108.      * This sequence matches.  If we've already got another match,
  2109.      * pick whichever is most specific.  Detail is most important,
  2110.      * then needMods.
  2111.      */
  2112.  
  2113.     if (bestPtr != NULL) {
  2114.         Pattern *patPtr2;
  2115.         int i;
  2116.  
  2117.         if (matchPtr->numPats != bestPtr->numPats) {
  2118.         if (bestPtr->numPats > matchPtr->numPats) {
  2119.             goto nextSequence;
  2120.         } else {
  2121.             goto newBest;
  2122.         }
  2123.         }
  2124.         for (i = 0, patPtr = matchPtr->pats, patPtr2 = bestPtr->pats;
  2125.             i < matchPtr->numPats; i++, patPtr++, patPtr2++) {
  2126.         if (patPtr->detail.clientData != patPtr2->detail.clientData) {
  2127.             if (patPtr->detail.clientData == 0) {
  2128.             goto nextSequence;
  2129.             } else {
  2130.             goto newBest;
  2131.             }
  2132.         }
  2133.         if (patPtr->needMods != patPtr2->needMods) {
  2134.             if ((patPtr->needMods & patPtr2->needMods)
  2135.                 == patPtr->needMods) {
  2136.             goto nextSequence;
  2137.             } else if ((patPtr->needMods & patPtr2->needMods)
  2138.                 == patPtr2->needMods) {
  2139.             goto newBest;
  2140.             }
  2141.         }
  2142.         }
  2143.         /*
  2144.          * Tie goes to current best pattern.
  2145.          *
  2146.          * (1) For virtual vs. virtual, the least recently defined
  2147.          * virtual wins, because virtuals are examined in order of
  2148.          * definition.  This order is _not_ guaranteed in the
  2149.          * documentation.
  2150.          *
  2151.          * (2) For virtual vs. physical, the physical wins because all
  2152.          * the physicals are examined before the virtuals.  This order
  2153.          * is guaranteed in the documentation.
  2154.          *
  2155.          * (3) For physical vs. physical pattern, the most recently
  2156.          * defined physical wins, because physicals are examined in
  2157.          * reverse order of definition.  This order is guaranteed in
  2158.          * the documentation.
  2159.          */
  2160.  
  2161.         goto nextSequence;    
  2162.     }
  2163.     newBest:
  2164.     bestPtr = matchPtr;
  2165.     bestSourcePtr = sourcePtr;
  2166.  
  2167.     nextSequence: continue;
  2168.     }
  2169.  
  2170.     *sourcePtrPtr = bestSourcePtr;
  2171.     return bestPtr;
  2172. }
  2173.  
  2174. /*
  2175.  *--------------------------------------------------------------
  2176.  *
  2177.  * ExpandPercents --
  2178.  *
  2179.  *    Given a command and an event, produce a new command
  2180.  *    by replacing % constructs in the original command
  2181.  *    with information from the X event.
  2182.  *
  2183.  * Results:
  2184.  *    The new expanded command is appended to the dynamic string
  2185.  *    given by dsPtr.
  2186.  *
  2187.  * Side effects:
  2188.  *    None.
  2189.  *
  2190.  *--------------------------------------------------------------
  2191.  */
  2192.  
  2193. static void
  2194. ExpandPercents(winPtr, before, eventPtr, keySym, dsPtr)
  2195.     TkWindow *winPtr;        /* Window where event occurred:  needed to
  2196.                  * get input context. */
  2197.     char *before;        /* Command containing percent expressions
  2198.                  * to be replaced. */
  2199.     XEvent *eventPtr;        /* X event containing information to be
  2200.                  * used in % replacements. */
  2201.     KeySym keySym;        /* KeySym: only relevant for KeyPress and
  2202.                  * KeyRelease events). */
  2203.     Tcl_DString *dsPtr;        /* Dynamic string in which to append new
  2204.                  * command. */
  2205. {
  2206.     int spaceNeeded, cvtFlags;    /* Used to substitute string as proper Tcl
  2207.                  * list element. */
  2208.     int number, flags, length;
  2209. #define NUM_SIZE 40
  2210.     char *string;
  2211.     char numStorage[NUM_SIZE+1];
  2212.  
  2213.     if (eventPtr->type < TK_LASTEVENT) {
  2214.     flags = flagArray[eventPtr->type];
  2215.     } else {
  2216.     flags = 0;
  2217.     }
  2218.     while (1) {
  2219.     /*
  2220.      * Find everything up to the next % character and append it
  2221.      * to the result string.
  2222.      */
  2223.  
  2224.     for (string = before; (*string != 0) && (*string != '%'); string++) {
  2225.         /* Empty loop body. */
  2226.     }
  2227.     if (string != before) {
  2228.         Tcl_DStringAppend(dsPtr, before, string-before);
  2229.         before = string;
  2230.     }
  2231.     if (*before == 0) {
  2232.         break;
  2233.     }
  2234.  
  2235.     /*
  2236.      * There's a percent sequence here.  Process it.
  2237.      */
  2238.  
  2239.     number = 0;
  2240.     string = "??";
  2241.     switch (before[1]) {
  2242.         case '#':
  2243.         number = eventPtr->xany.serial;
  2244.         goto doNumber;
  2245.         case 'a':
  2246.         TkpPrintWindowId(numStorage, eventPtr->xconfigure.above);
  2247.         string = numStorage;
  2248.         goto doString;
  2249.         case 'b':
  2250.         number = eventPtr->xbutton.button;
  2251.         goto doNumber;
  2252.         case 'c':
  2253.         if (flags & EXPOSE) {
  2254.             number = eventPtr->xexpose.count;
  2255.         }
  2256.         goto doNumber;
  2257.         case 'd':
  2258.         if (flags & (CROSSING|FOCUS)) {
  2259.             if (flags & FOCUS) {
  2260.             number = eventPtr->xfocus.detail;
  2261.             } else {
  2262.             number = eventPtr->xcrossing.detail;
  2263.             }
  2264.             string = TkFindStateString(notifyDetail, number);
  2265.         }
  2266.         goto doString;
  2267.         case 'f':
  2268.         number = eventPtr->xcrossing.focus;
  2269.         goto doNumber;
  2270.         case 'h':
  2271.         if (flags & EXPOSE) {
  2272.             number = eventPtr->xexpose.height;
  2273.         } else if (flags & (CONFIG)) {
  2274.             number = eventPtr->xconfigure.height;
  2275.         }
  2276.         goto doNumber;
  2277.         case 'k':
  2278.         number = eventPtr->xkey.keycode;
  2279.         goto doNumber;
  2280.         case 'm':
  2281.         if (flags & CROSSING) {
  2282.             number = eventPtr->xcrossing.mode;
  2283.         } else if (flags & FOCUS) {
  2284.             number = eventPtr->xfocus.mode;
  2285.         }
  2286.         string = TkFindStateString(notifyMode, number);
  2287.         goto doString;
  2288.         case 'o':
  2289.         if (flags & CREATE) {
  2290.             number = eventPtr->xcreatewindow.override_redirect;
  2291.         } else if (flags & MAP) {
  2292.             number = eventPtr->xmap.override_redirect;
  2293.         } else if (flags & REPARENT) {
  2294.             number = eventPtr->xreparent.override_redirect;
  2295.         } else if (flags & CONFIG) {
  2296.             number = eventPtr->xconfigure.override_redirect;
  2297.         }
  2298.         goto doNumber;
  2299.         case 'p':
  2300.         string = TkFindStateString(circPlace, eventPtr->xcirculate.place);
  2301.         goto doString;
  2302.         case 's':
  2303.         if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
  2304.             number = eventPtr->xkey.state;
  2305.         } else if (flags & CROSSING) {
  2306.             number = eventPtr->xcrossing.state;
  2307.         } else if (flags & VISIBILITY) {
  2308.             string = TkFindStateString(visNotify,
  2309.                 eventPtr->xvisibility.state);
  2310.             goto doString;
  2311.         }
  2312.         goto doNumber;
  2313.         case 't':
  2314.         if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
  2315.             number = (int) eventPtr->xkey.time;
  2316.         } else if (flags & CROSSING) {
  2317.             number = (int) eventPtr->xcrossing.time;
  2318.         } else if (flags & PROP) {
  2319.             number = (int) eventPtr->xproperty.time;
  2320.         }
  2321.         goto doNumber;
  2322.         case 'v':
  2323.         number = eventPtr->xconfigurerequest.value_mask;
  2324.         goto doNumber;
  2325.         case 'w':
  2326.         if (flags & EXPOSE) {
  2327.             number = eventPtr->xexpose.width;
  2328.         } else if (flags & CONFIG) {
  2329.             number = eventPtr->xconfigure.width;
  2330.         }
  2331.         goto doNumber;
  2332.         case 'x':
  2333.         if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
  2334.             number = eventPtr->xkey.x;
  2335.         } else if (flags & CROSSING) {
  2336.             number = eventPtr->xcrossing.x;
  2337.         } else if (flags & EXPOSE) {
  2338.             number = eventPtr->xexpose.x;
  2339.         } else if (flags & (CREATE|CONFIG|GRAVITY)) {
  2340.             number = eventPtr->xcreatewindow.x;
  2341.         } else if (flags & REPARENT) {
  2342.             number = eventPtr->xreparent.x;
  2343.         }
  2344.         goto doNumber;
  2345.         case 'y':
  2346.         if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
  2347.             number = eventPtr->xkey.y;
  2348.         } else if (flags & EXPOSE) {
  2349.             number = eventPtr->xexpose.y;
  2350.         } else if (flags & (CREATE|CONFIG|GRAVITY)) {
  2351.             number = eventPtr->xcreatewindow.y;
  2352.         } else if (flags & REPARENT) {
  2353.             number = eventPtr->xreparent.y;
  2354.         } else if (flags & CROSSING) {
  2355.             number = eventPtr->xcrossing.y;
  2356.  
  2357.         }
  2358.         goto doNumber;
  2359.         case 'A':
  2360.         if (flags & KEY) {
  2361.             int numChars;
  2362.  
  2363.             /*
  2364.              * If we're using input methods and this is a keypress
  2365.              * event, invoke XmbTkFindStateString.  Otherwise just use
  2366.              * the older XTkFindStateString.
  2367.              */
  2368.  
  2369. #ifdef TK_USE_INPUT_METHODS
  2370.             Status status;
  2371.             if ((winPtr->inputContext != NULL)
  2372.                 && (eventPtr->type == KeyPress)) {
  2373.                         numChars = XmbLookupString(winPtr->inputContext,
  2374.                                 &eventPtr->xkey, numStorage, NUM_SIZE,
  2375.                                 (KeySym *) NULL, &status);
  2376.             if ((status != XLookupChars)
  2377.                 && (status != XLookupBoth)) {
  2378.                 numChars = 0;
  2379.             }
  2380.                     } else {
  2381.                         numChars = XLookupString(&eventPtr->xkey, numStorage,
  2382.                                 NUM_SIZE, (KeySym *) NULL,
  2383.                                 (XComposeStatus *) NULL);
  2384.             }
  2385. #else /* TK_USE_INPUT_METHODS */
  2386.             numChars = XLookupString(&eventPtr->xkey, numStorage,
  2387.                 NUM_SIZE, (KeySym *) NULL,
  2388.                 (XComposeStatus *) NULL);
  2389. #endif /* TK_USE_INPUT_METHODS */
  2390.             numStorage[numChars] = '\0';
  2391.             string = numStorage;
  2392.         }
  2393.         goto doString;
  2394.         case 'B':
  2395.         number = eventPtr->xcreatewindow.border_width;
  2396.         goto doNumber;
  2397.         case 'E':
  2398.         number = (int) eventPtr->xany.send_event;
  2399.         goto doNumber;
  2400.         case 'K':
  2401.         if (flags & KEY) {
  2402.             char *name;
  2403.  
  2404.             name = TkKeysymToString(keySym);
  2405.             if (name != NULL) {
  2406.             string = name;
  2407.             }
  2408.         }
  2409.         goto doString;
  2410.         case 'N':
  2411.         number = (int) keySym;
  2412.         goto doNumber;
  2413.         case 'R':
  2414.         TkpPrintWindowId(numStorage, eventPtr->xkey.root);
  2415.         string = numStorage;
  2416.         goto doString;
  2417.             case 'S':
  2418.         TkpPrintWindowId(numStorage, eventPtr->xkey.subwindow);
  2419.         string = numStorage;
  2420.         goto doString;
  2421.         case 'T':
  2422.         number = eventPtr->type;
  2423.         goto doNumber;
  2424.         case 'W': {
  2425.         Tk_Window tkwin;
  2426.  
  2427.         tkwin = Tk_IdToWindow(eventPtr->xany.display,
  2428.             eventPtr->xany.window);
  2429.         if (tkwin != NULL) {
  2430.             string = Tk_PathName(tkwin);
  2431.         } else {
  2432.             string = "??";
  2433.         }
  2434.         goto doString;
  2435.         }
  2436.         case 'X': {
  2437.         Tk_Window tkwin;
  2438.         int x, y;
  2439.         int width, height;
  2440.  
  2441.         number = eventPtr->xkey.x_root;
  2442.         tkwin = Tk_IdToWindow(eventPtr->xany.display,
  2443.             eventPtr->xany.window);
  2444.         if (tkwin != NULL) {
  2445.             Tk_GetVRootGeometry(tkwin, &x, &y, &width, &height);
  2446.             number -= x;
  2447.         }
  2448.         goto doNumber;
  2449.         }
  2450.         case 'Y': {
  2451.         Tk_Window tkwin;
  2452.         int x, y;
  2453.         int width, height;
  2454.  
  2455.         number = eventPtr->xkey.y_root;
  2456.         tkwin = Tk_IdToWindow(eventPtr->xany.display,
  2457.             eventPtr->xany.window);
  2458.         if (tkwin != NULL) {
  2459.             Tk_GetVRootGeometry(tkwin, &x, &y, &width, &height);
  2460.             number -= y;
  2461.         }
  2462.         goto doNumber;
  2463.         }
  2464.         default:
  2465.         numStorage[0] = before[1];
  2466.         numStorage[1] = '\0';
  2467.         string = numStorage;
  2468.         goto doString;
  2469.     }
  2470.  
  2471.     doNumber:
  2472.     sprintf(numStorage, "%d", number);
  2473.     string = numStorage;
  2474.  
  2475.     doString:
  2476.     spaceNeeded = Tcl_ScanElement(string, &cvtFlags);
  2477.     length = Tcl_DStringLength(dsPtr);
  2478.     Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
  2479.     spaceNeeded = Tcl_ConvertElement(string,
  2480.         Tcl_DStringValue(dsPtr) + length,
  2481.         cvtFlags | TCL_DONT_USE_BRACES);
  2482.     Tcl_DStringSetLength(dsPtr, length + spaceNeeded);
  2483.     before += 2;
  2484.     }
  2485. }
  2486.  
  2487. /*
  2488.  *----------------------------------------------------------------------
  2489.  *
  2490.  * ChangeScreen --
  2491.  *
  2492.  *    This procedure is invoked whenever the current screen changes
  2493.  *    in an application.  It invokes a Tcl procedure named
  2494.  *    "tkScreenChanged", passing it the screen name as argument.
  2495.  *    tkScreenChanged does things like making the tkPriv variable
  2496.  *    point to an array for the current display.
  2497.  *
  2498.  * Results:
  2499.  *    None.
  2500.  *
  2501.  * Side effects:
  2502.  *    Depends on what tkScreenChanged does.  If an error occurs
  2503.  *    them tkError will be invoked.
  2504.  *
  2505.  *----------------------------------------------------------------------
  2506.  */
  2507.  
  2508. static void
  2509. ChangeScreen(interp, dispName, screenIndex)
  2510.     Tcl_Interp *interp;            /* Interpreter in which to invoke
  2511.                      * command. */
  2512.     char *dispName;            /* Name of new display. */
  2513.     int screenIndex;            /* Index of new screen. */
  2514. {
  2515.     Tcl_DString cmd;
  2516.     int code;
  2517.     char screen[30];
  2518.  
  2519.     Tcl_DStringInit(&cmd);
  2520.     Tcl_DStringAppend(&cmd, "tkScreenChanged ", 16);
  2521.     Tcl_DStringAppend(&cmd, dispName, -1);
  2522.     sprintf(screen, ".%d", screenIndex);
  2523.     Tcl_DStringAppend(&cmd, screen, -1);
  2524.     code = Tcl_GlobalEval(interp, Tcl_DStringValue(&cmd));
  2525.     if (code != TCL_OK) {
  2526.     Tcl_AddErrorInfo(interp,
  2527.         "\n    (changing screen in event binding)");
  2528.     Tcl_BackgroundError(interp);
  2529.     }
  2530. }
  2531.  
  2532.  
  2533. /*
  2534.  *----------------------------------------------------------------------
  2535.  *
  2536.  * Tk_EventCmd --
  2537.  *
  2538.  *    This procedure is invoked to process the "event" Tcl command.
  2539.  *    It is used to define and generate events.
  2540.  *
  2541.  * Results:
  2542.  *    A standard Tcl result.
  2543.  *
  2544.  * Side effects:
  2545.  *    See the user documentation.
  2546.  *
  2547.  *----------------------------------------------------------------------
  2548.  */
  2549.  
  2550. int
  2551. Tk_EventCmd(clientData, interp, argc, argv)
  2552.     ClientData clientData;    /* Main window associated with
  2553.                  * interpreter. */
  2554.     Tcl_Interp *interp;        /* Current interpreter. */
  2555.     int argc;            /* Number of arguments. */
  2556.     char **argv;        /* Argument strings. */
  2557. {
  2558.     int i;
  2559.     size_t length;
  2560.     char *option;
  2561.     Tk_Window tkwin;
  2562.     VirtualEventTable *vetPtr;
  2563.     TkBindInfo bindInfo;
  2564.  
  2565.     if (argc < 2) {
  2566.     Tcl_AppendResult(interp, "wrong # args: should be \"",
  2567.         argv[0], " option ?arg1?\"", (char *) NULL);
  2568.     return TCL_ERROR;
  2569.     }
  2570.  
  2571.     option = argv[1];
  2572.     length = strlen(option);
  2573.     if (length == 0) {
  2574.     goto badopt;
  2575.     }
  2576.  
  2577.     tkwin = (Tk_Window) clientData;
  2578.     bindInfo = ((TkWindow *) tkwin)->mainPtr->bindInfo;
  2579.     vetPtr = &((BindInfo *) bindInfo)->virtualEventTable;
  2580.  
  2581.     if (strncmp(option, "add", length) == 0) {
  2582.     if (argc < 4) {
  2583.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  2584.             " add virtual sequence ?sequence ...?\"", (char *) NULL);
  2585.         return TCL_ERROR;
  2586.     }
  2587.     for (i = 3; i < argc; i++) {
  2588.         if (CreateVirtualEvent(interp, vetPtr, argv[2], argv[i])
  2589.             != TCL_OK) {
  2590.         return TCL_ERROR;
  2591.         }
  2592.     }
  2593.     } else if (strncmp(option, "delete", length) == 0) {
  2594.     if (argc < 3) {
  2595.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  2596.             " delete virtual ?sequence sequence ...?\"",
  2597.             (char *) NULL);
  2598.         return TCL_ERROR;
  2599.     }
  2600.     if (argc == 3) {
  2601.         return DeleteVirtualEvent(interp, vetPtr, argv[2], NULL);
  2602.     }
  2603.     for (i = 3; i < argc; i++) {
  2604.         if (DeleteVirtualEvent(interp, vetPtr, argv[2], argv[i])
  2605.             != TCL_OK) {
  2606.         return TCL_ERROR;
  2607.         }
  2608.     }
  2609.     } else if (strncmp(option, "generate", length) == 0) {
  2610.     if (argc < 4) {
  2611.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  2612.             " generate window event ?options?\"", (char *) NULL);
  2613.         return TCL_ERROR;
  2614.     }
  2615.     return HandleEventGenerate(interp, tkwin, argc - 2, argv + 2);
  2616.     } else if (strncmp(option, "info", length) == 0) {
  2617.     if (argc == 2) {
  2618.         GetAllVirtualEvents(interp, vetPtr);
  2619.         return TCL_OK;
  2620.     } else if (argc == 3) {    
  2621.         return GetVirtualEvent(interp, vetPtr, argv[2]);
  2622.     } else {
  2623.         Tcl_AppendResult(interp, "wrong # args: should be \"", argv[0],
  2624.             " info ?virtual?\"", (char *) NULL);
  2625.         return TCL_ERROR;
  2626.     }
  2627.     } else {
  2628.     badopt:
  2629.     Tcl_AppendResult(interp, "bad option \"", argv[1],
  2630.         "\": should be add, delete, generate, info", (char *) NULL);
  2631.     return TCL_ERROR;
  2632.     }
  2633.     return TCL_OK;
  2634. }
  2635.  
  2636. /*
  2637.  *---------------------------------------------------------------------------
  2638.  *
  2639.  * InitVirtualEventTable --
  2640.  *
  2641.  *    Given storage for a virtual event table, set up the fields to
  2642.  *    prepare a new domain in which virtual events may be defined.
  2643.  *
  2644.  * Results:
  2645.  *    None.
  2646.  *
  2647.  * Side effects:
  2648.  *    *vetPtr is now initialized.
  2649.  *
  2650.  *---------------------------------------------------------------------------
  2651.  */
  2652.  
  2653. static void
  2654. InitVirtualEventTable(vetPtr)
  2655.     VirtualEventTable *vetPtr;    /* Pointer to virtual event table.  Memory
  2656.                  * is supplied by the caller. */
  2657. {
  2658.     Tcl_InitHashTable(&vetPtr->patternTable,
  2659.         sizeof(PatternTableKey) / sizeof(int));
  2660.     Tcl_InitHashTable(&vetPtr->nameTable, TCL_ONE_WORD_KEYS);
  2661. }
  2662.  
  2663. /*
  2664.  *---------------------------------------------------------------------------
  2665.  *
  2666.  * DeleteVirtualEventTable --
  2667.  *
  2668.  *    Delete the contents of a virtual event table.  The caller is
  2669.  *    responsible for freeing any memory used by the table itself.
  2670.  *
  2671.  * Results:
  2672.  *    None.
  2673.  *
  2674.  * Side effects:
  2675.  *    Memory is freed.
  2676.  *
  2677.  *---------------------------------------------------------------------------
  2678.  */
  2679.  
  2680. static void
  2681. DeleteVirtualEventTable(vetPtr)
  2682.     VirtualEventTable *vetPtr;    /* The virtual event table to delete. */
  2683. {
  2684.     Tcl_HashEntry *hPtr;
  2685.     Tcl_HashSearch search;
  2686.     PatSeq *psPtr, *nextPtr;
  2687.  
  2688.     hPtr = Tcl_FirstHashEntry(&vetPtr->patternTable, &search);
  2689.     for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
  2690.     psPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
  2691.     for ( ; psPtr != NULL; psPtr = nextPtr) {
  2692.         nextPtr = psPtr->nextSeqPtr;
  2693.         ckfree((char *) psPtr->voPtr);
  2694.         ckfree((char *) psPtr);
  2695.     }
  2696.     }
  2697.     Tcl_DeleteHashTable(&vetPtr->patternTable);
  2698.  
  2699.     hPtr = Tcl_FirstHashEntry(&vetPtr->nameTable, &search);
  2700.     for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
  2701.         ckfree((char *) Tcl_GetHashValue(hPtr));
  2702.     }
  2703.     Tcl_DeleteHashTable(&vetPtr->nameTable);
  2704. }
  2705.  
  2706. /*
  2707.  *----------------------------------------------------------------------
  2708.  *
  2709.  * CreateVirtualEvent --
  2710.  *
  2711.  *    Add a new definition for a virtual event.  If the virtual event
  2712.  *    is already defined, the new definition augments those that
  2713.  *    already exist.
  2714.  *
  2715.  * Results:
  2716.  *    The return value is TCL_ERROR if an error occured while
  2717.  *    creating the virtual binding.  In this case, an error message
  2718.  *    will be left in interp->result.  If all went well then the return
  2719.  *    value is TCL_OK.
  2720.  *
  2721.  * Side effects:
  2722.  *    The virtual event may cause future calls to Tk_BindEvent to
  2723.  *    behave differently than they did previously.
  2724.  *
  2725.  *----------------------------------------------------------------------
  2726.  */
  2727.  
  2728. static int
  2729. CreateVirtualEvent(interp, vetPtr, virtString, eventString)
  2730.     Tcl_Interp *interp;        /* Used for error reporting. */
  2731.     VirtualEventTable *vetPtr;/* Table in which to augment virtual event. */
  2732.     char *virtString;        /* Name of new virtual event. */
  2733.     char *eventString;        /* String describing physical event that
  2734.                  * triggers virtual event. */
  2735. {
  2736.     PatSeq *psPtr;
  2737.     int dummy;
  2738.     Tcl_HashEntry *vhPtr;
  2739.     unsigned long eventMask;
  2740.     PhysicalsOwned *poPtr;
  2741.     VirtualOwners *voPtr;
  2742.     Tk_Uid virtUid;
  2743.     
  2744.     virtUid = GetVirtualEventUid(interp, virtString);
  2745.     if (virtUid == NULL) {
  2746.         return TCL_ERROR;
  2747.     }
  2748.  
  2749.     /*
  2750.      * Find/create physical event
  2751.      */
  2752.  
  2753.     psPtr = FindSequence(interp, &vetPtr->patternTable, NULL, eventString,
  2754.         1, 0, &eventMask);
  2755.     if (psPtr == NULL) {
  2756.         return TCL_ERROR;
  2757.     }
  2758.  
  2759.     /*
  2760.      * Find/create virtual event.
  2761.      */
  2762.  
  2763.     vhPtr = Tcl_CreateHashEntry(&vetPtr->nameTable, virtUid, &dummy);
  2764.  
  2765.     /*
  2766.      * Make virtual event own the physical event.
  2767.      */
  2768.  
  2769.     poPtr = (PhysicalsOwned *) Tcl_GetHashValue(vhPtr);
  2770.     if (poPtr == NULL) {
  2771.     poPtr = (PhysicalsOwned *) ckalloc(sizeof(PhysicalsOwned));
  2772.     poPtr->numOwned = 0;
  2773.     } else {
  2774.         /*
  2775.      * See if this virtual event is already defined for this physical
  2776.      * event and just return if it is.
  2777.      */
  2778.  
  2779.     int i;
  2780.     for (i = 0; i < poPtr->numOwned; i++) {
  2781.         if (poPtr->patSeqs[i] == psPtr) {
  2782.             return TCL_OK;
  2783.         }
  2784.     }
  2785.     poPtr = (PhysicalsOwned *) ckrealloc((char *) poPtr,
  2786.         sizeof(PhysicalsOwned) + poPtr->numOwned * sizeof(PatSeq *));
  2787.     }    
  2788.     Tcl_SetHashValue(vhPtr, (ClientData) poPtr);
  2789.     poPtr->patSeqs[poPtr->numOwned] = psPtr;
  2790.     poPtr->numOwned++;
  2791.  
  2792.     /*
  2793.      * Make physical event so it can trigger the virtual event.
  2794.      */
  2795.  
  2796.     voPtr = psPtr->voPtr;
  2797.     if (voPtr == NULL) {
  2798.         voPtr = (VirtualOwners *) ckalloc(sizeof(VirtualOwners));
  2799.     voPtr->numOwners = 0;
  2800.     } else {
  2801.         voPtr = (VirtualOwners *) ckrealloc((char *) voPtr,
  2802.         sizeof(VirtualOwners)
  2803.         + voPtr->numOwners * sizeof(Tcl_HashEntry *));
  2804.     }
  2805.     psPtr->voPtr = voPtr;
  2806.     voPtr->owners[voPtr->numOwners] = vhPtr;
  2807.     voPtr->numOwners++;
  2808.  
  2809.     return TCL_OK;
  2810. }
  2811.  
  2812. /*
  2813.  *--------------------------------------------------------------
  2814.  *
  2815.  * DeleteVirtualEvent --
  2816.  *
  2817.  *    Remove the definition of a given virtual event.  If the 
  2818.  *    event string is NULL, all definitions of the virtual event
  2819.  *    will be removed.  Otherwise, just the specified definition
  2820.  *    of the virtual event will be removed.
  2821.  *
  2822.  * Results:
  2823.  *    The result is a standard Tcl return value.  If an error
  2824.  *    occurs then interp->result will contain an error message.
  2825.  *    It is not an error to attempt to delete a virtual event that
  2826.  *    does not exist or a definition that does not exist.
  2827.  *
  2828.  * Side effects:
  2829.  *    The virtual event given by virtString may be removed from the
  2830.  *    virtual event table.  
  2831.  *
  2832.  *--------------------------------------------------------------
  2833.  */
  2834.  
  2835. static int
  2836. DeleteVirtualEvent(interp, vetPtr, virtString, eventString)
  2837.     Tcl_Interp *interp;        /* Used for error reporting. */
  2838.     VirtualEventTable *vetPtr;/* Table in which to delete event. */
  2839.     char *virtString;        /* String describing event sequence that
  2840.                  * triggers binding. */
  2841.     char *eventString;        /* The event sequence that should be deleted,
  2842.                  * or NULL to delete all event sequences for
  2843.                  * the entire virtual event. */
  2844. {
  2845.     int iPhys;
  2846.     Tk_Uid virtUid;
  2847.     Tcl_HashEntry *vhPtr;
  2848.     PhysicalsOwned *poPtr;
  2849.     PatSeq *eventPSPtr;
  2850.  
  2851.     virtUid = GetVirtualEventUid(interp, virtString);
  2852.     if (virtUid == NULL) {
  2853.         return TCL_ERROR;
  2854.     }
  2855.     
  2856.     vhPtr = Tcl_FindHashEntry(&vetPtr->nameTable, virtUid);
  2857.     if (vhPtr == NULL) {
  2858.         return TCL_OK;
  2859.     }
  2860.     poPtr = (PhysicalsOwned *) Tcl_GetHashValue(vhPtr);
  2861.  
  2862.     eventPSPtr = NULL;
  2863.     if (eventString != NULL) {
  2864.     unsigned long eventMask;
  2865.  
  2866.     /*
  2867.      * Delete only the specific physical event associated with the
  2868.      * virtual event.  If the physical event doesn't already exist, or
  2869.      * the virtual event doesn't own that physical event, return w/o
  2870.      * doing anything.
  2871.      */
  2872.  
  2873.     eventPSPtr = FindSequence(interp, &vetPtr->patternTable, NULL,
  2874.         eventString, 0, 0, &eventMask);
  2875.     if (eventPSPtr == NULL) {
  2876.         return (interp->result[0] != '\0') ? TCL_ERROR : TCL_OK;
  2877.     }
  2878.     }
  2879.  
  2880.     for (iPhys = poPtr->numOwned; --iPhys >= 0; ) {
  2881.     PatSeq *psPtr = poPtr->patSeqs[iPhys];
  2882.     if ((eventPSPtr == NULL) || (psPtr == eventPSPtr)) {
  2883.         int iVirt;
  2884.         VirtualOwners *voPtr;
  2885.         
  2886.         /*
  2887.          * Remove association between this physical event and the given
  2888.          * virtual event that it triggers.
  2889.          */
  2890.  
  2891.         voPtr = psPtr->voPtr;
  2892.         for (iVirt = 0; iVirt < voPtr->numOwners; iVirt++) {
  2893.         if (voPtr->owners[iVirt] == vhPtr) {
  2894.             break;
  2895.         }
  2896.         }
  2897.         if (iVirt == voPtr->numOwners) {
  2898.         panic("DeleteVirtualEvent: couldn't find owner");
  2899.         }
  2900.         voPtr->numOwners--;
  2901.         if (voPtr->numOwners == 0) {
  2902.         /*
  2903.          * Removed last reference to this physical event, so
  2904.          * remove it from physical->virtual map.
  2905.          */
  2906.         PatSeq *prevPtr = (PatSeq *) Tcl_GetHashValue(psPtr->hPtr);
  2907.         if (prevPtr == psPtr) {
  2908.             if (psPtr->nextSeqPtr == NULL) {
  2909.             Tcl_DeleteHashEntry(psPtr->hPtr);
  2910.             } else {
  2911.             Tcl_SetHashValue(psPtr->hPtr,
  2912.                 psPtr->nextSeqPtr);
  2913.             }
  2914.         } else {
  2915.             for ( ; ; prevPtr = prevPtr->nextSeqPtr) {
  2916.             if (prevPtr == NULL) {
  2917.                 panic("Tk_DeleteVirtualEvent couldn't find on hash chain");
  2918.             }
  2919.             if (prevPtr->nextSeqPtr == psPtr) {
  2920.                 prevPtr->nextSeqPtr = psPtr->nextSeqPtr;
  2921.                 break;
  2922.             }
  2923.             }
  2924.         }
  2925.         ckfree((char *) psPtr->voPtr);
  2926.         ckfree((char *) psPtr);
  2927.         } else {
  2928.         /*
  2929.          * This physical event still triggers some other virtual
  2930.          * event(s).  Consolidate the list of virtual owners for
  2931.          * this physical event so it no longer triggers the
  2932.          * given virtual event.
  2933.          */
  2934.         voPtr->owners[iVirt] = voPtr->owners[voPtr->numOwners];
  2935.         }
  2936.  
  2937.         /*
  2938.          * Now delete the virtual event's reference to the physical
  2939.          * event.
  2940.          */
  2941.  
  2942.         poPtr->numOwned--;
  2943.         if (eventPSPtr != NULL && poPtr->numOwned != 0) {
  2944.             /*
  2945.          * Just deleting this one physical event.  Consolidate list
  2946.          * of owned physical events and return.
  2947.          */
  2948.  
  2949.         poPtr->patSeqs[iPhys] = poPtr->patSeqs[poPtr->numOwned];
  2950.         return TCL_OK;
  2951.         }
  2952.     }
  2953.     }
  2954.  
  2955.     if (poPtr->numOwned == 0) {
  2956.     /*
  2957.      * All the physical events for this virtual event were deleted,
  2958.      * either because there was only one associated physical event or
  2959.      * because the caller was deleting the entire virtual event.  Now
  2960.      * the virtual event itself should be deleted.
  2961.      */
  2962.  
  2963.     ckfree((char *) poPtr);
  2964.     Tcl_DeleteHashEntry(vhPtr);
  2965.     }
  2966.     return TCL_OK;
  2967. }
  2968.  
  2969. /*
  2970.  *---------------------------------------------------------------------------
  2971.  *
  2972.  * GetVirtualEvent --
  2973.  *
  2974.  *    Return the list of physical events that can invoke the
  2975.  *    given virtual event.
  2976.  *
  2977.  * Results:
  2978.  *    The return value is TCL_OK and interp->result is filled with the
  2979.  *    string representation of the physical events associated with the
  2980.  *    virtual event; if there are no physical events for the given virtual
  2981.  *    event, interp->result is filled with and empty string.  If the
  2982.  *    virtual event string is improperly formed, then TCL_ERROR is
  2983.  *    returned and an error message is left in interp->result.
  2984.  *
  2985.  * Side effects:
  2986.  *    None.
  2987.  *
  2988.  *---------------------------------------------------------------------------
  2989.  */
  2990.  
  2991. static int
  2992. GetVirtualEvent(interp, vetPtr, virtString)
  2993.     Tcl_Interp *interp;        /* Interpreter for reporting. */
  2994.     VirtualEventTable *vetPtr;/* Table in which to look for event. */
  2995.     char *virtString;        /* String describing virtual event. */
  2996. {
  2997.     Tcl_HashEntry *vhPtr;
  2998.     Tcl_DString ds;
  2999.     int iPhys;
  3000.     PhysicalsOwned *poPtr;
  3001.     Tk_Uid virtUid;
  3002.  
  3003.     virtUid = GetVirtualEventUid(interp, virtString);
  3004.     if (virtUid == NULL) {
  3005.         return TCL_ERROR;
  3006.     }
  3007.  
  3008.     vhPtr = Tcl_FindHashEntry(&vetPtr->nameTable, virtUid);
  3009.     if (vhPtr == NULL) {
  3010.         return TCL_OK;
  3011.     }
  3012.  
  3013.     Tcl_DStringInit(&ds);
  3014.  
  3015.     poPtr = (PhysicalsOwned *) Tcl_GetHashValue(vhPtr);
  3016.     for (iPhys = 0; iPhys < poPtr->numOwned; iPhys++) {
  3017.     Tcl_DStringSetLength(&ds, 0);
  3018.     GetPatternString(poPtr->patSeqs[iPhys], &ds);
  3019.     Tcl_AppendElement(interp, Tcl_DStringValue(&ds));
  3020.     }
  3021.     Tcl_DStringFree(&ds);
  3022.  
  3023.     return TCL_OK;
  3024. }
  3025.  
  3026. /*
  3027.  *--------------------------------------------------------------
  3028.  *
  3029.  * GetAllVirtualEvents --
  3030.  *
  3031.  *    Return a list that contains the names of all the virtual
  3032.  *    event defined.
  3033.  *
  3034.  * Results:
  3035.  *    There is no return value.  Interp->result is modified to
  3036.  *    hold a Tcl list with one entry for each virtual event in 
  3037.  *    nameTable.  
  3038.  *
  3039.  * Side effects:
  3040.  *    None.
  3041.  *
  3042.  *--------------------------------------------------------------
  3043.  */
  3044.  
  3045. static void
  3046. GetAllVirtualEvents(interp, vetPtr)
  3047.     Tcl_Interp *interp;        /* Interpreter returning result. */
  3048.     VirtualEventTable *vetPtr;/* Table containing events. */
  3049. {
  3050.     Tcl_HashEntry *hPtr;
  3051.     Tcl_HashSearch search;
  3052.     Tcl_DString ds;
  3053.  
  3054.     Tcl_DStringInit(&ds);
  3055.  
  3056.     hPtr = Tcl_FirstHashEntry(&vetPtr->nameTable, &search);
  3057.     for ( ; hPtr != NULL; hPtr = Tcl_NextHashEntry(&search)) {
  3058.     Tcl_DStringSetLength(&ds, 0);    
  3059.     Tcl_DStringAppend(&ds, "<<", 2);
  3060.     Tcl_DStringAppend(&ds, Tcl_GetHashKey(hPtr->tablePtr, hPtr), -1);
  3061.     Tcl_DStringAppend(&ds, ">>", 2);
  3062.         Tcl_AppendElement(interp, Tcl_DStringValue(&ds));
  3063.     }
  3064.  
  3065.     Tcl_DStringFree(&ds);
  3066. }
  3067.  
  3068. /*
  3069.  *---------------------------------------------------------------------------
  3070.  *
  3071.  * HandleEventGenerate --
  3072.  *
  3073.  *    Helper function for the "event generate" command.  Generate and
  3074.  *    process an XEvent, constructed from information parsed from the
  3075.  *    event description string and its optional arguments.
  3076.  *
  3077.  *    argv[0] contains name of the target window.
  3078.  *    argv[1] contains pattern string for one event (e.g, <Control-v>).
  3079.  *    argv[2..argc-1] contains -field/option pairs for specifying
  3080.  *                additional detail in the generated event.
  3081.  *
  3082.  *    Either virtual or physical events can be generated this way.
  3083.  *    The event description string must contain the specification
  3084.  *    for only one event.
  3085.  *
  3086.  * Results:
  3087.  *    None.
  3088.  *
  3089.  * Side effects:
  3090.  *    When constructing the event, 
  3091.  *     event.xany.serial is filled with the current X serial number.
  3092.  *     event.xany.window is filled with the target window.
  3093.  *     event.xany.display is filled with the target window's display.
  3094.  *    Any other fields in eventPtr which are not specified by the pattern
  3095.  *    string or the optional arguments, are set to 0.
  3096.  *
  3097.  *    The event may be handled sychronously or asynchronously, depending
  3098.  *    on the value specified by the optional "-when" option.  The
  3099.  *    default setting is synchronous.
  3100.  *
  3101.  *---------------------------------------------------------------------------
  3102.  */
  3103. static int
  3104. HandleEventGenerate(interp, main, argc, argv)
  3105.     Tcl_Interp *interp;        /* Interp for error messages and name lookup. */
  3106.     Tk_Window main;        /* Main window associated with interp. */
  3107.     int argc;            /* Number of arguments. */
  3108.     char **argv;        /* Argument strings. */
  3109. {
  3110.     Pattern pat;
  3111.     Tk_Window tkwin;
  3112.     char *p;
  3113.     unsigned long eventMask;
  3114.     int count, i, state, flags, synch;
  3115.     Tcl_QueuePosition pos;
  3116.     XEvent event;    
  3117.  
  3118.     if (argv[0][0] == '.') {
  3119.     tkwin = Tk_NameToWindow(interp, argv[0], main);
  3120.     if (tkwin == NULL) {
  3121.         return TCL_ERROR;
  3122.     }
  3123.     } else {
  3124.     if (TkpScanWindowId(NULL, argv[0], &i) != TCL_OK) {
  3125.         Tcl_AppendResult(interp, "bad window name/identifier \"",
  3126.             argv[0], "\"", (char *) NULL);
  3127.         return TCL_ERROR;
  3128.     }
  3129.     tkwin = Tk_IdToWindow(Tk_Display(main), (Window) i);
  3130.     if ((tkwin == NULL) || (((TkWindow *) main)->mainPtr
  3131.         != ((TkWindow *) tkwin)->mainPtr)) {
  3132.         Tcl_AppendResult(interp, "window id \"", argv[0],
  3133.             "\" doesn't exist in this application", (char *) NULL);
  3134.         return TCL_ERROR;
  3135.     }
  3136.     }
  3137.  
  3138.     p = argv[1];
  3139.     count = ParseEventDescription(interp, &p, &pat, &eventMask);
  3140.     if (count == 0) {
  3141.     return TCL_ERROR;
  3142.     }
  3143.     if (count != 1) {
  3144.     interp->result = "Double or Triple modifier not allowed";
  3145.     return TCL_ERROR;
  3146.     }
  3147.     if (*p != '\0') {
  3148.     interp->result = "only one event specification allowed";
  3149.     return TCL_ERROR;
  3150.     }
  3151.     if (argc & 1) {
  3152.         Tcl_AppendResult(interp, "value for \"", argv[argc - 1],
  3153.         "\" missing", (char *) NULL);
  3154.     return TCL_ERROR;
  3155.     }
  3156.  
  3157.     memset((VOID *) &event, 0, sizeof(event));
  3158.     event.xany.type = pat.eventType;
  3159.     event.xany.serial = NextRequest(Tk_Display(tkwin));
  3160.     event.xany.send_event = False;
  3161.     event.xany.window = Tk_WindowId(tkwin);
  3162.     event.xany.display = Tk_Display(tkwin);
  3163.  
  3164.     flags = flagArray[event.xany.type];
  3165.     if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
  3166.     event.xkey.state = pat.needMods;
  3167.     if (flags & KEY) {
  3168.         /*
  3169.          * When mapping from a keysym to a keycode, need information about
  3170.          * the modifier state that should be used so that when they call 
  3171.          * XKeycodeToKeysym    taking into account the xkey.state, they will
  3172.          * get back the original keysym.  
  3173.          */
  3174.  
  3175.         if (pat.detail.keySym == NoSymbol) {
  3176.             event.xkey.keycode = 0;
  3177.         } else {
  3178.         event.xkey.keycode = XKeysymToKeycode(event.xany.display,
  3179.             pat.detail.keySym);
  3180.         }
  3181.         if (event.xkey.keycode != 0) {
  3182.         for (state = 0; state < 4; state++) {
  3183.             if (XKeycodeToKeysym(event.xany.display,
  3184.                 event.xkey.keycode, state) == pat.detail.keySym) {
  3185.             if (state & 1) {
  3186.                 event.xkey.state |= ShiftMask;
  3187.             }
  3188.             if (state & 2) {
  3189.                 TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr; 
  3190.                 event.xkey.state |= dispPtr->modeModMask;
  3191.             }
  3192.             break;
  3193.             }
  3194.         }
  3195.         }
  3196.     } else if (flags & BUTTON) {
  3197.         event.xbutton.button = pat.detail.button;
  3198.     } else if (flags & VIRTUAL) {
  3199.         ((XVirtualEvent *) &event)->name = pat.detail.name;
  3200.     }
  3201.     }
  3202.     if (flags & (CREATE|DESTROY|UNMAP|MAP|REPARENT|CONFIG|GRAVITY|CIRC)) {
  3203.     event.xcreatewindow.window = event.xany.window;
  3204.     }
  3205.  
  3206.     /*
  3207.      * Process the remaining arguments to fill in additional fields
  3208.      * of the event.
  3209.      */
  3210.  
  3211.     synch = 1;
  3212.     pos = TCL_QUEUE_TAIL;
  3213.     for (i = 2; i < argc; i += 2) {
  3214.     char *field, *value;
  3215.     Tk_Window tkwin2;
  3216.     int number;
  3217.     KeySym keysym;
  3218.     
  3219.     field = argv[i];
  3220.     value = argv[i+1];
  3221.  
  3222.     if (strcmp(field, "-when") == 0) {
  3223.         if (strcmp(value, "now") == 0) {
  3224.         synch = 1;
  3225.         } else if (strcmp(value, "head") == 0) {
  3226.         pos = TCL_QUEUE_HEAD;
  3227.         synch = 0;
  3228.         } else if (strcmp(value, "mark") == 0) {
  3229.         pos = TCL_QUEUE_MARK;
  3230.         synch = 0;
  3231.         } else if (strcmp(value, "tail") == 0) {
  3232.         pos = TCL_QUEUE_TAIL;
  3233.         synch = 0;
  3234.         } else {
  3235.         Tcl_AppendResult(interp, "bad position \"", value,
  3236.             "\": should be now, head, mark, tail", (char *) NULL);
  3237.         return TCL_ERROR;
  3238.         }
  3239.     } else if (strcmp(field, "-above") == 0) {
  3240.         if (value[0] == '.') {
  3241.         tkwin2 = Tk_NameToWindow(interp, value, main);
  3242.         if (tkwin2 == NULL) {
  3243.             return TCL_ERROR;
  3244.         }
  3245.         number = Tk_WindowId(tkwin2);
  3246.         } else if (TkpScanWindowId(interp, value, &number)
  3247.             != TCL_OK) {
  3248.         return TCL_ERROR;
  3249.         }
  3250.         if (flags & CONFIG) {
  3251.         event.xconfigure.above = number;
  3252.         } else {
  3253.         goto badopt;
  3254.         }
  3255.     } else if (strcmp(field, "-borderwidth") == 0) {
  3256.         if (Tk_GetPixels(interp, tkwin, value, &number) != TCL_OK) {
  3257.         return TCL_ERROR;
  3258.         }
  3259.         if (flags & (CREATE|CONFIG)) {
  3260.         event.xcreatewindow.border_width = number;
  3261.         } else {
  3262.         goto badopt;
  3263.         }
  3264.     } else if (strcmp(field, "-button") == 0) {
  3265.         if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
  3266.         return TCL_ERROR;
  3267.         }
  3268.         if (flags & BUTTON) {
  3269.             event.xbutton.button = number;
  3270.         } else {
  3271.         goto badopt;
  3272.         }
  3273.     } else if (strcmp(field, "-count") == 0) {
  3274.         if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
  3275.         return TCL_ERROR;
  3276.         }
  3277.         if (flags & EXPOSE) {
  3278.         event.xexpose.count = number;
  3279.         } else {
  3280.         goto badopt;
  3281.         }
  3282.     } else if (strcmp(field, "-detail") == 0) {
  3283.         number = TkFindStateNum(interp, field, notifyDetail, value);
  3284.         if (number < 0) {
  3285.         return TCL_ERROR;
  3286.         }
  3287.         if (flags & FOCUS) {
  3288.         event.xfocus.detail = number;
  3289.         } else if (flags & CROSSING) {
  3290.         event.xcrossing.detail = number;
  3291.         } else {
  3292.         goto badopt;
  3293.         }
  3294.     } else if (strcmp(field, "-focus") == 0) {
  3295.         if (Tcl_GetBoolean(interp, value, &number) != TCL_OK) {
  3296.         return TCL_ERROR;
  3297.         }
  3298.         if (flags & CROSSING) {
  3299.         event.xcrossing.focus = number;
  3300.         } else {
  3301.         goto badopt;
  3302.         }
  3303.     } else if (strcmp(field, "-height") == 0) {
  3304.         if (Tk_GetPixels(interp, tkwin, value, &number) != TCL_OK) {
  3305.         return TCL_ERROR;
  3306.         }
  3307.         if (flags & EXPOSE) {
  3308.          event.xexpose.height = number;
  3309.         } else if (flags & CONFIG) {
  3310.         event.xconfigure.height = number;
  3311.         } else {
  3312.         goto badopt;
  3313.         }
  3314.     } else if (strcmp(field, "-keycode") == 0) {
  3315.         if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
  3316.         return TCL_ERROR;
  3317.         }
  3318.         if (flags & KEY) {
  3319.             event.xkey.keycode = number;
  3320.         } else {
  3321.         goto badopt;
  3322.         }
  3323.     } else if (strcmp(field, "-keysym") == 0) {
  3324.         keysym = TkStringToKeysym(value);
  3325.         if (keysym == NoSymbol) {
  3326.         Tcl_AppendResult(interp, "unknown keysym \"", value,
  3327.             "\"", (char *) NULL);
  3328.         return TCL_ERROR;
  3329.         }
  3330.         /*
  3331.          * When mapping from a keysym to a keycode, need information about
  3332.          * the modifier state that should be used so that when they call 
  3333.          * XKeycodeToKeysym    taking into account the xkey.state, they will
  3334.          * get back the original keysym.  
  3335.          */
  3336.  
  3337.         number = XKeysymToKeycode(event.xany.display, keysym);
  3338.         if (number == 0) {
  3339.         Tcl_AppendResult(interp, "no keycode for keysym \"", value,
  3340.             "\"", (char *) NULL);
  3341.         return TCL_ERROR;
  3342.         }
  3343.         for (state = 0; state < 4; state++) {
  3344.         if (XKeycodeToKeysym(event.xany.display, (unsigned) number,
  3345.             state) == keysym) {
  3346.             if (state & 1) {
  3347.             event.xkey.state |= ShiftMask;
  3348.             }
  3349.             if (state & 2) {
  3350.             TkDisplay *dispPtr = ((TkWindow *) tkwin)->dispPtr; 
  3351.             event.xkey.state |= dispPtr->modeModMask;
  3352.             }
  3353.             break;
  3354.         }
  3355.         }        
  3356.         if (flags & KEY) {
  3357.         event.xkey.keycode = number;
  3358.         } else {
  3359.         goto badopt;
  3360.         }
  3361.     } else if (strcmp(field, "-mode") == 0) {
  3362.         number = TkFindStateNum(interp, field, notifyMode, value);
  3363.         if (number < 0) {
  3364.         return TCL_ERROR;
  3365.         }
  3366.         if (flags & CROSSING) {
  3367.         event.xcrossing.mode = number;
  3368.         } else if (flags & FOCUS) {
  3369.         event.xfocus.mode = number;
  3370.         } else {
  3371.         goto badopt;
  3372.         }
  3373.     } else if (strcmp(field, "-override") == 0) {
  3374.         if (Tcl_GetBoolean(interp, value, &number) != TCL_OK) {
  3375.         return TCL_ERROR;
  3376.         }
  3377.         if (flags & CREATE) {
  3378.         event.xcreatewindow.override_redirect = number;
  3379.         } else if (flags & MAP) {
  3380.         event.xmap.override_redirect = number;
  3381.         } else if (flags & REPARENT) {
  3382.         event.xreparent.override_redirect = number;
  3383.         } else if (flags & CONFIG) {
  3384.         event.xconfigure.override_redirect = number;
  3385.         } else {
  3386.         goto badopt;
  3387.         }
  3388.     } else if (strcmp(field, "-place") == 0) {
  3389.         number = TkFindStateNum(interp, field, circPlace, value);
  3390.         if (number < 0) {
  3391.         return TCL_ERROR;
  3392.         }
  3393.         if (flags & CIRC) {
  3394.         event.xcirculate.place = number;
  3395.         } else {
  3396.         goto badopt;
  3397.         }
  3398.     } else if (strcmp(field, "-root") == 0) {
  3399.         if (value[0] == '.') {
  3400.         tkwin2 = Tk_NameToWindow(interp, value, main);
  3401.         if (tkwin2 == NULL) {
  3402.             return TCL_ERROR;
  3403.         }
  3404.         number = Tk_WindowId(tkwin2);
  3405.         } else if (TkpScanWindowId(interp, value, &number)
  3406.             != TCL_OK) {
  3407.         return TCL_ERROR;
  3408.         }
  3409.         if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
  3410.         event.xkey.root = number;
  3411.         } else {
  3412.         goto badopt;
  3413.         }
  3414.     } else if (strcmp(field, "-rootx") == 0) {
  3415.         if (Tk_GetPixels(interp, tkwin, value, &number) != TCL_OK) {
  3416.         return TCL_ERROR;
  3417.         }
  3418.         if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
  3419.         event.xkey.x_root = number;
  3420.         } else {
  3421.         goto badopt;
  3422.         }
  3423.     } else if (strcmp(field, "-rooty") == 0) {
  3424.         if (Tk_GetPixels(interp, tkwin, value, &number) != TCL_OK) {
  3425.         return TCL_ERROR;
  3426.         }
  3427.         if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
  3428.         event.xkey.y_root = number;
  3429.         } else {
  3430.         goto badopt;
  3431.         }
  3432.     } else if (strcmp(field, "-sendevent") == 0) {
  3433.         if (isdigit(UCHAR(value[0]))) {
  3434.         /*
  3435.          * Allow arbitrary integer values for the field; they
  3436.          * are needed by a few of the tests in the Tk test suite.
  3437.          */
  3438.  
  3439.         if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
  3440.             return TCL_ERROR;
  3441.         }
  3442.         } else {
  3443.         if (Tcl_GetBoolean(interp, value, &number) != TCL_OK) {
  3444.             return TCL_ERROR;
  3445.         }
  3446.         }
  3447.         event.xany.send_event = number;
  3448.     } else if (strcmp(field, "-serial") == 0) {
  3449.         if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
  3450.         return TCL_ERROR;
  3451.         }
  3452.         event.xany.serial = number;
  3453.     } else if (strcmp(field, "-state") == 0) {
  3454.         if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
  3455.         if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
  3456.             return TCL_ERROR;
  3457.         }
  3458.         if (flags & (KEY_BUTTON_MOTION_VIRTUAL)) {
  3459.             event.xkey.state = number;
  3460.         } else {
  3461.             event.xcrossing.state = number;
  3462.         }
  3463.         } else if (flags & VISIBILITY) {
  3464.         number = TkFindStateNum(interp, field, visNotify, value);
  3465.         if (number < 0) {
  3466.             return TCL_ERROR;
  3467.         }
  3468.         event.xvisibility.state = number;
  3469.         } else {
  3470.         goto badopt;
  3471.         }        
  3472.     } else if (strcmp(field, "-subwindow") == 0) {
  3473.         if (value[0] == '.') {
  3474.         tkwin2 = Tk_NameToWindow(interp, value, main);
  3475.         if (tkwin2 == NULL) {
  3476.             return TCL_ERROR;
  3477.         }
  3478.         number = Tk_WindowId(tkwin2);
  3479.         } else if (TkpScanWindowId(interp, value, &number)
  3480.             != TCL_OK) {
  3481.         return TCL_ERROR;
  3482.         }
  3483.         if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
  3484.         event.xkey.subwindow = number;
  3485.         } else {
  3486.         goto badopt;
  3487.         }
  3488.     } else if (strcmp(field, "-time") == 0) {
  3489.         if (Tcl_GetInt(interp, value, &number) != TCL_OK) {
  3490.         return TCL_ERROR;
  3491.         }
  3492.         if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
  3493.         event.xkey.time = (Time) number;
  3494.         } else if (flags & PROP) {
  3495.         event.xproperty.time = (Time) number;
  3496.         } else {
  3497.         goto badopt;
  3498.         }
  3499.     } else if (strcmp(field, "-width") == 0) {
  3500.         if (Tk_GetPixels(interp, tkwin, value, &number) != TCL_OK) {
  3501.         return TCL_ERROR;
  3502.         }
  3503.         if (flags & EXPOSE) {
  3504.         event.xexpose.width = number;
  3505.         } else if (flags & (CREATE|CONFIG)) {
  3506.         event.xcreatewindow.width = number;
  3507.         } else {
  3508.         goto badopt;
  3509.         }
  3510.     } else if (strcmp(field, "-window") == 0) {
  3511.         if (value[0] == '.') {
  3512.         tkwin2 = Tk_NameToWindow(interp, value, main);
  3513.         if (tkwin2 == NULL) {
  3514.             return TCL_ERROR;
  3515.         }
  3516.         number = Tk_WindowId(tkwin2);
  3517.         } else if (TkpScanWindowId(interp, value, &number)
  3518.             != TCL_OK) {
  3519.         return TCL_ERROR;
  3520.         }
  3521.         if (flags & (CREATE|DESTROY|UNMAP|MAP|REPARENT|CONFIG
  3522.             |GRAVITY|CIRC)) {
  3523.         event.xcreatewindow.window = number;
  3524.         } else {
  3525.         goto badopt;
  3526.         }
  3527.     } else if (strcmp(field, "-x") == 0) {
  3528.         int rootX, rootY;
  3529.         if (Tk_GetPixels(interp, tkwin, value, &number) != TCL_OK) {
  3530.         return TCL_ERROR;
  3531.         }
  3532.         Tk_GetRootCoords(tkwin, &rootX, &rootY);
  3533.         rootX += number;
  3534.         if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {    
  3535.         event.xkey.x = number;
  3536.         event.xkey.x_root = rootX;
  3537.         } else if (flags & EXPOSE) {
  3538.         event.xexpose.x = number;
  3539.         } else if (flags & (CREATE|CONFIG|GRAVITY)) { 
  3540.         event.xcreatewindow.x = number;
  3541.         } else if (flags & REPARENT) {        
  3542.         event.xreparent.x = number;
  3543.         } else {
  3544.         goto badopt;
  3545.         }
  3546.     } else if (strcmp(field, "-y") == 0) {
  3547.         int rootX, rootY;
  3548.         if (Tk_GetPixels(interp, tkwin, value, &number) != TCL_OK) {
  3549.         return TCL_ERROR;
  3550.         }
  3551.         Tk_GetRootCoords(tkwin, &rootX, &rootY);
  3552.         rootY += number;
  3553.         if (flags & (KEY_BUTTON_MOTION_VIRTUAL|CROSSING)) {
  3554.         event.xkey.y = number;
  3555.         event.xkey.y_root = rootY;
  3556.         } else if (flags & EXPOSE) {
  3557.         event.xexpose.y = number;
  3558.         } else if (flags & (CREATE|CONFIG|GRAVITY)) {
  3559.         event.xcreatewindow.y = number;
  3560.         } else if (flags & REPARENT) {
  3561.         event.xreparent.y = number;
  3562.         } else {
  3563.         goto badopt;
  3564.         }
  3565.     } else {
  3566.         badopt:
  3567.         Tcl_AppendResult(interp, "bad option to ", argv[1],
  3568.             " event: \"", field, "\"", (char *) NULL);
  3569.         return TCL_ERROR;
  3570.     }
  3571.     }
  3572.  
  3573.     if (synch != 0) {
  3574.     Tk_HandleEvent(&event);
  3575.     } else {
  3576.     Tk_QueueWindowEvent(&event, pos);
  3577.     }
  3578.     Tcl_ResetResult(interp);
  3579.     return TCL_OK;
  3580. }
  3581.  
  3582. /*
  3583.  *-------------------------------------------------------------------------
  3584.  *
  3585.  * GetVirtualEventUid --
  3586.  *
  3587.  *    Determine if the given string is in the proper format for a
  3588.  *    virtual event.
  3589.  *
  3590.  * Results:
  3591.  *    The return value is NULL if the virtual event string was
  3592.  *    not in the proper format.  In this case, an error message
  3593.  *    will be left in interp->result.  Otherwise the return
  3594.  *    value is a Tk_Uid that represents the virtual event.
  3595.  *
  3596.  * Side effects:
  3597.  *    None.
  3598.  *
  3599.  *-------------------------------------------------------------------------
  3600.  */
  3601. static Tk_Uid
  3602. GetVirtualEventUid(interp, virtString)
  3603.     Tcl_Interp *interp;
  3604.     char *virtString;
  3605. {
  3606.     Tk_Uid uid;
  3607.     int length;
  3608.  
  3609.     length = strlen(virtString);
  3610.  
  3611.     if (length < 5 || virtString[0] != '<' || virtString[1] != '<' ||
  3612.         virtString[length - 2] != '>' || virtString[length - 1] != '>') {
  3613.         Tcl_AppendResult(interp, "virtual event \"", virtString,
  3614.         "\" is badly formed", (char *) NULL);
  3615.         return NULL;
  3616.     }
  3617.     virtString[length - 2] = '\0';
  3618.     uid = Tk_GetUid(virtString + 2);
  3619.     virtString[length - 2] = '>';
  3620.  
  3621.     return uid;
  3622. }
  3623.  
  3624.  
  3625. /*
  3626.  *----------------------------------------------------------------------
  3627.  *
  3628.  * FindSequence --
  3629.  *
  3630.  *    Find the entry in the pattern table that corresponds to a
  3631.  *    particular pattern string, and return a pointer to that
  3632.  *    entry.
  3633.  *
  3634.  * Results:
  3635.  *    The return value is normally a pointer to the PatSeq
  3636.  *    in patternTable that corresponds to eventString.  If an error
  3637.  *    was found while parsing eventString, or if "create" is 0 and
  3638.  *    no pattern sequence previously existed, then NULL is returned
  3639.  *    and interp->result contains a message describing the problem.
  3640.  *    If no pattern sequence previously existed for eventString, then
  3641.  *    a new one is created with a NULL command field.  In a successful
  3642.  *    return, *maskPtr is filled in with a mask of the event types
  3643.  *    on which the pattern sequence depends.
  3644.  *
  3645.  * Side effects:
  3646.  *    A new pattern sequence may be allocated.
  3647.  *
  3648.  *----------------------------------------------------------------------
  3649.  */
  3650.  
  3651. static PatSeq *
  3652. FindSequence(interp, patternTablePtr, object, eventString, create,
  3653.     allowVirtual, maskPtr)
  3654.     Tcl_Interp *interp;        /* Interpreter to use for error
  3655.                  * reporting. */
  3656.     Tcl_HashTable *patternTablePtr; /* Table to use for lookup. */
  3657.     ClientData object;        /* For binding table, token for object with
  3658.                  * which binding is associated.
  3659.                  * For virtual event table, NULL. */
  3660.     char *eventString;        /* String description of pattern to
  3661.                  * match on.  See user documentation
  3662.                  * for details. */
  3663.     int create;            /* 0 means don't create the entry if
  3664.                  * it doesn't already exist.   Non-zero
  3665.                  * means create. */
  3666.     int allowVirtual;        /* 0 means that virtual events are not
  3667.                  * allowed in the sequence.  Non-zero
  3668.                  * otherwise. */
  3669.     unsigned long *maskPtr;    /* *maskPtr is filled in with the event
  3670.                  * types on which this pattern sequence
  3671.                  * depends. */
  3672. {
  3673.  
  3674.     Pattern pats[EVENT_BUFFER_SIZE];
  3675.     int numPats, virtualFound;
  3676.     char *p;
  3677.     Pattern *patPtr;
  3678.     PatSeq *psPtr;
  3679.     Tcl_HashEntry *hPtr;
  3680.     int flags, count, new;
  3681.     size_t sequenceSize;
  3682.     unsigned long eventMask;
  3683.     PatternTableKey key;
  3684.  
  3685.     /*
  3686.      *-------------------------------------------------------------
  3687.      * Step 1: parse the pattern string to produce an array
  3688.      * of Patterns.  The array is generated backwards, so
  3689.      * that the lowest-indexed pattern corresponds to the last
  3690.      * event that must occur.
  3691.      *-------------------------------------------------------------
  3692.      */
  3693.  
  3694.     p = eventString;
  3695.     flags = 0;
  3696.     eventMask = 0;
  3697.     virtualFound = 0;
  3698.  
  3699.     patPtr = &pats[EVENT_BUFFER_SIZE-1];
  3700.     for (numPats = 0; numPats < EVENT_BUFFER_SIZE; numPats++, patPtr--) {
  3701.     while (isspace(UCHAR(*p))) {
  3702.         p++;
  3703.     }
  3704.     if (*p == '\0') {
  3705.         break;
  3706.     }
  3707.  
  3708.     count = ParseEventDescription(interp, &p, patPtr, &eventMask);
  3709.     if (count == 0) {
  3710.         return NULL;
  3711.     }
  3712.  
  3713.     if (eventMask & VirtualEventMask) {
  3714.         if (allowVirtual == 0) {
  3715.         interp->result =
  3716.             "virtual event not allowed in definition of another virtual event";
  3717.         return NULL;
  3718.         }
  3719.         virtualFound = 1;
  3720.     }
  3721.  
  3722.     /*
  3723.      * Replicate events for DOUBLE and TRIPLE.
  3724.      */
  3725.  
  3726.     if ((count > 1) && (numPats < EVENT_BUFFER_SIZE-1)) {
  3727.         flags |= PAT_NEARBY;
  3728.         patPtr[-1] = patPtr[0];
  3729.         patPtr--;
  3730.         numPats++;
  3731.         if ((count == 3) && (numPats < EVENT_BUFFER_SIZE-1)) {
  3732.         patPtr[-1] = patPtr[0];
  3733.         patPtr--;
  3734.         numPats++;
  3735.         }
  3736.     }
  3737.     }
  3738.  
  3739.     /*
  3740.      *-------------------------------------------------------------
  3741.      * Step 2: find the sequence in the binding table if it exists,
  3742.      * and add a new sequence to the table if it doesn't.
  3743.      *-------------------------------------------------------------
  3744.      */
  3745.  
  3746.     if (numPats == 0) {
  3747.     interp->result = "no events specified in binding";
  3748.     return NULL;
  3749.     }
  3750.     if ((numPats > 1) && (virtualFound != 0)) {
  3751.         interp->result = "virtual events may not be composed";
  3752.     return NULL;
  3753.     }
  3754.     
  3755.     patPtr = &pats[EVENT_BUFFER_SIZE-numPats];
  3756.     memset(&key, 0, sizeof(key));
  3757.     key.object = object;
  3758.     key.type = patPtr->eventType;
  3759.     key.detail = patPtr->detail;
  3760.     hPtr = Tcl_CreateHashEntry(patternTablePtr, (char *) &key, &new);
  3761.     sequenceSize = numPats*sizeof(Pattern);
  3762.     if (!new) {
  3763.     for (psPtr = (PatSeq *) Tcl_GetHashValue(hPtr); psPtr != NULL;
  3764.         psPtr = psPtr->nextSeqPtr) {
  3765.         if ((numPats == psPtr->numPats)
  3766.             && ((flags & PAT_NEARBY) == (psPtr->flags & PAT_NEARBY))
  3767.             && (memcmp((char *) patPtr, (char *) psPtr->pats,
  3768.             sequenceSize) == 0)) {
  3769.         goto done;
  3770.         }
  3771.     }
  3772.     }
  3773.     if (!create) {
  3774.     if (new) {
  3775.         Tcl_DeleteHashEntry(hPtr);
  3776.     }
  3777.     return NULL;
  3778.     }
  3779.     psPtr = (PatSeq *) ckalloc((unsigned) (sizeof(PatSeq)
  3780.         + (numPats-1)*sizeof(Pattern)));
  3781.     psPtr->numPats = numPats;
  3782.     psPtr->eventProc = NULL;
  3783.     psPtr->freeProc = NULL;
  3784.     psPtr->clientData = NULL;
  3785.     psPtr->flags = flags;
  3786.     psPtr->refCount = 0;
  3787.     psPtr->nextSeqPtr = (PatSeq *) Tcl_GetHashValue(hPtr);
  3788.     psPtr->hPtr = hPtr;
  3789.     psPtr->voPtr = NULL;
  3790.     psPtr->nextObjPtr = NULL;
  3791.     Tcl_SetHashValue(hPtr, psPtr);
  3792.  
  3793.     memcpy((VOID *) psPtr->pats, (VOID *) patPtr, sequenceSize);
  3794.  
  3795.     done:
  3796.     *maskPtr = eventMask;
  3797.     return psPtr;
  3798. }
  3799.  
  3800. /*
  3801.  *---------------------------------------------------------------------------
  3802.  *
  3803.  * ParseEventDescription --
  3804.  *
  3805.  *    Fill Pattern buffer with information about event from
  3806.  *    event string.
  3807.  *
  3808.  * Results:
  3809.  *    Leaves error message in interp and returns 0 if there was an
  3810.  *    error due to a badly formed event string.  Returns 1 if proper
  3811.  *    event was specified, 2 if Double modifier was used in event
  3812.  *    string, or 3 if Triple was used.
  3813.  *
  3814.  * Side effects:
  3815.  *    On exit, eventStringPtr points to rest of event string (after the
  3816.  *    closing '>', so that this procedure can be called repeatedly to
  3817.  *    parse all the events in the entire sequence.
  3818.  *
  3819.  *---------------------------------------------------------------------------
  3820.  */
  3821.  
  3822. static int
  3823. ParseEventDescription(interp, eventStringPtr, patPtr,
  3824.     eventMaskPtr)
  3825.     Tcl_Interp *interp;        /* For error messages. */
  3826.     char **eventStringPtr;    /* On input, holds a pointer to start of
  3827.                  * event string.  On exit, gets pointer to
  3828.                  * rest of string after parsed event. */
  3829.     Pattern *patPtr;        /* Filled with the pattern parsed from the
  3830.                  * event string. */
  3831.     unsigned long *eventMaskPtr;/* Filled with event mask of matched event. */
  3832.                  
  3833. {
  3834.     char *p;
  3835.     unsigned long eventMask;
  3836.     int count, eventFlags;
  3837. #define FIELD_SIZE 48
  3838.     char field[FIELD_SIZE];
  3839.     Tcl_HashEntry *hPtr;
  3840.  
  3841.     p = *eventStringPtr;
  3842.  
  3843.     patPtr->eventType = -1;
  3844.     patPtr->needMods = 0;
  3845.     patPtr->detail.clientData = 0;
  3846.  
  3847.     eventMask = 0;
  3848.     count = 1;
  3849.     
  3850.     /*
  3851.      * Handle simple ASCII characters.
  3852.      */
  3853.  
  3854.     if (*p != '<') {
  3855.     char string[2];
  3856.  
  3857.     patPtr->eventType = KeyPress;
  3858.     eventMask = KeyPressMask;
  3859.     string[0] = *p;
  3860.     string[1] = 0;
  3861.     patPtr->detail.keySym = TkStringToKeysym(string);
  3862.     if (patPtr->detail.keySym == NoSymbol) {
  3863.         if (isprint(UCHAR(*p))) {
  3864.         patPtr->detail.keySym = *p;
  3865.         } else {
  3866.         sprintf(interp->result,
  3867.             "bad ASCII character 0x%x", (unsigned char) *p);
  3868.         return 0;
  3869.         }
  3870.     }
  3871.     p++;
  3872.     goto end;
  3873.     }
  3874.  
  3875.     /*
  3876.      * A fancier event description.  This can be either a virtual event
  3877.      * or a physical event.
  3878.      *
  3879.      * A virtual event description consists of:
  3880.      *
  3881.      * 1. double open angle brackets.
  3882.      * 2. virtual event name.
  3883.      * 3. double close angle brackets.
  3884.      *
  3885.      * A physical event description consists of:
  3886.      *
  3887.      * 1. open angle bracket.
  3888.      * 2. any number of modifiers, each followed by spaces
  3889.      *    or dashes.
  3890.      * 3. an optional event name.
  3891.      * 4. an option button or keysym name.  Either this or
  3892.      *    item 3 *must* be present;  if both are present
  3893.      *    then they are separated by spaces or dashes.
  3894.      * 5. a close angle bracket.
  3895.      */
  3896.  
  3897.     p++;
  3898.     if (*p == '<') {
  3899.     /*
  3900.      * This is a virtual event: soak up all the characters up to
  3901.      * the next '>'.
  3902.      */
  3903.  
  3904.     char *field = p + 1;        
  3905.     p = strchr(field, '>');
  3906.     if (p == field) {
  3907.         interp->result = "virtual event \"<<>>\" is badly formed";
  3908.         return 0;
  3909.     }        
  3910.     if ((p == NULL) || (p[1] != '>')) {
  3911.         interp->result = "missing \">\" in virtual binding";
  3912.         return 0;
  3913.     }
  3914.     *p = '\0';
  3915.     patPtr->eventType = VirtualEvent;
  3916.     eventMask = VirtualEventMask;
  3917.     patPtr->detail.name = Tk_GetUid(field);
  3918.     *p = '>';
  3919.  
  3920.     p += 2;
  3921.     goto end;
  3922.     }
  3923.  
  3924.     while (1) {
  3925.     ModInfo *modPtr;
  3926.     p = GetField(p, field, FIELD_SIZE);
  3927.     if (*p == '>') {
  3928.         /*
  3929.          * This solves the problem of, e.g., <Control-M> being
  3930.          * misinterpreted as Control + Meta + missing keysym
  3931.          * instead of Control + KeyPress + M.
  3932.          */
  3933.          break;
  3934.     }
  3935.     hPtr = Tcl_FindHashEntry(&modTable, field);
  3936.     if (hPtr == NULL) {
  3937.         break;
  3938.     }
  3939.     modPtr = (ModInfo *) Tcl_GetHashValue(hPtr);
  3940.     patPtr->needMods |= modPtr->mask;
  3941.     if (modPtr->flags & (DOUBLE|TRIPLE)) {
  3942.         if (modPtr->flags & DOUBLE) {
  3943.         count = 2;
  3944.         } else {
  3945.         count = 3;
  3946.         }
  3947.     }
  3948.     while ((*p == '-') || isspace(UCHAR(*p))) {
  3949.         p++;
  3950.     }
  3951.     }
  3952.  
  3953.     eventFlags = 0;
  3954.     hPtr = Tcl_FindHashEntry(&eventTable, field);
  3955.     if (hPtr != NULL) {
  3956.     EventInfo *eiPtr;
  3957.     eiPtr = (EventInfo *) Tcl_GetHashValue(hPtr);
  3958.  
  3959.     patPtr->eventType = eiPtr->type;
  3960.     eventFlags = flagArray[eiPtr->type];
  3961.     eventMask = eiPtr->eventMask;
  3962.     while ((*p == '-') || isspace(UCHAR(*p))) {
  3963.         p++;
  3964.     }
  3965.     p = GetField(p, field, FIELD_SIZE);
  3966.     }
  3967.     if (*field != '\0') {
  3968.     if ((*field >= '1') && (*field <= '5') && (field[1] == '\0')) {
  3969.         if (eventFlags == 0) {
  3970.         patPtr->eventType = ButtonPress;
  3971.         eventMask = ButtonPressMask;
  3972.         } else if (eventFlags & KEY) {
  3973.         goto getKeysym;
  3974.         } else if ((eventFlags & BUTTON) == 0) {
  3975.         Tcl_AppendResult(interp, "specified button \"", field,
  3976.             "\" for non-button event", (char *) NULL);
  3977.         return 0;
  3978.         }
  3979.         patPtr->detail.button = (*field - '0');
  3980.     } else {
  3981.         getKeysym:
  3982.         patPtr->detail.keySym = TkStringToKeysym(field);
  3983.         if (patPtr->detail.keySym == NoSymbol) {
  3984.         Tcl_AppendResult(interp, "bad event type or keysym \"",
  3985.             field, "\"", (char *) NULL);
  3986.         return 0;
  3987.         }
  3988.         if (eventFlags == 0) {
  3989.         patPtr->eventType = KeyPress;
  3990.         eventMask = KeyPressMask;
  3991.         } else if ((eventFlags & KEY) == 0) {
  3992.         Tcl_AppendResult(interp, "specified keysym \"", field,
  3993.             "\" for non-key event", (char *) NULL);
  3994.         return 0;
  3995.         }
  3996.     }
  3997.     } else if (eventFlags == 0) {
  3998.     interp->result = "no event type or button # or keysym";
  3999.     return 0;
  4000.     }
  4001.  
  4002.     while ((*p == '-') || isspace(UCHAR(*p))) {
  4003.     p++;
  4004.     }
  4005.     if (*p != '>') {
  4006.     while (*p != '\0') {
  4007.         p++;
  4008.         if (*p == '>') {
  4009.         interp->result = "extra characters after detail in binding";
  4010.         return 0;
  4011.         }
  4012.     }
  4013.     interp->result = "missing \">\" in binding";
  4014.     return 0;
  4015.     }
  4016.     p++;
  4017.  
  4018. end:
  4019.     *eventStringPtr = p;
  4020.     *eventMaskPtr |= eventMask;
  4021.     return count;
  4022. }
  4023.  
  4024. /*
  4025.  *----------------------------------------------------------------------
  4026.  *
  4027.  * GetField --
  4028.  *
  4029.  *    Used to parse pattern descriptions.  Copies up to
  4030.  *    size characters from p to copy, stopping at end of
  4031.  *    string, space, "-", ">", or whenever size is
  4032.  *    exceeded.
  4033.  *
  4034.  * Results:
  4035.  *    The return value is a pointer to the character just
  4036.  *    after the last one copied (usually "-" or space or
  4037.  *    ">", but could be anything if size was exceeded).
  4038.  *    Also places NULL-terminated string (up to size
  4039.  *    character, including NULL), at copy.
  4040.  *
  4041.  * Side effects:
  4042.  *    None.
  4043.  *
  4044.  *----------------------------------------------------------------------
  4045.  */
  4046.  
  4047. static char *
  4048. GetField(p, copy, size)
  4049.     char *p;        /* Pointer to part of pattern. */
  4050.     char *copy;    /* Place to copy field. */
  4051.     int size;            /* Maximum number of characters to
  4052.                  * copy. */
  4053. {
  4054.     while ((*p != '\0') && !isspace(UCHAR(*p)) && (*p != '>')
  4055.         && (*p != '-') && (size > 1)) {
  4056.     *copy = *p;
  4057.     p++;
  4058.     copy++;
  4059.     size--;
  4060.     }
  4061.     *copy = '\0';
  4062.     return p;
  4063. }
  4064.  
  4065. /*
  4066.  *---------------------------------------------------------------------------
  4067.  *
  4068.  * GetPatternString --
  4069.  *
  4070.  *    Produce a string version of the given event, for displaying to
  4071.  *    the user.  
  4072.  *
  4073.  * Results:
  4074.  *    The string is left in dsPtr.
  4075.  *
  4076.  * Side effects:
  4077.  *    It is the caller's responsibility to initialize the DString before
  4078.  *    and to free it after calling this procedure.
  4079.  *
  4080.  *---------------------------------------------------------------------------
  4081.  */
  4082. static void
  4083. GetPatternString(psPtr, dsPtr)
  4084.     PatSeq *psPtr;
  4085.     Tcl_DString *dsPtr;
  4086. {
  4087.     Pattern *patPtr;
  4088.     char c, buffer[10];
  4089.     int patsLeft, needMods;
  4090.     ModInfo *modPtr;
  4091.     EventInfo *eiPtr;
  4092.  
  4093.     /*
  4094.      * The order of the patterns in the sequence is backwards from the order
  4095.      * in which they must be output.
  4096.      */
  4097.  
  4098.     for (patsLeft = psPtr->numPats, patPtr = &psPtr->pats[psPtr->numPats - 1];
  4099.         patsLeft > 0; patsLeft--, patPtr--) {
  4100.  
  4101.     /*
  4102.      * Check for simple case of an ASCII character.
  4103.      */
  4104.  
  4105.     if ((patPtr->eventType == KeyPress)
  4106.         && ((psPtr->flags & PAT_NEARBY) == 0) 
  4107.         && (patPtr->needMods == 0)
  4108.         && (patPtr->detail.keySym < 128)
  4109.         && isprint(UCHAR(patPtr->detail.keySym))
  4110.         && (patPtr->detail.keySym != '<')
  4111.         && (patPtr->detail.keySym != ' ')) {
  4112.  
  4113.         c = (char) patPtr->detail.keySym;
  4114.         Tcl_DStringAppend(dsPtr, &c, 1);
  4115.         continue;
  4116.     }
  4117.  
  4118.     /*
  4119.      * Check for virtual event.
  4120.      */
  4121.  
  4122.     if (patPtr->eventType == VirtualEvent) {
  4123.         Tcl_DStringAppend(dsPtr, "<<", 2);
  4124.         Tcl_DStringAppend(dsPtr, patPtr->detail.name, -1);
  4125.         Tcl_DStringAppend(dsPtr, ">>", 2);
  4126.         continue;
  4127.     }
  4128.  
  4129.     /*
  4130.      * It's a more general event specification.  First check
  4131.      * for "Double" or "Triple", then modifiers, then event type,
  4132.      * then keysym or button detail.
  4133.      */
  4134.  
  4135.     Tcl_DStringAppend(dsPtr, "<", 1);
  4136.     if ((psPtr->flags & PAT_NEARBY) && (patsLeft > 1)
  4137.         && (memcmp((char *) patPtr, (char *) (patPtr-1),
  4138.             sizeof(Pattern)) == 0)) {
  4139.         patsLeft--;
  4140.         patPtr--;
  4141.         if ((patsLeft > 1) && (memcmp((char *) patPtr,
  4142.             (char *) (patPtr-1), sizeof(Pattern)) == 0)) {
  4143.         patsLeft--;
  4144.         patPtr--;
  4145.         Tcl_DStringAppend(dsPtr, "Triple-", 7);
  4146.         } else {
  4147.         Tcl_DStringAppend(dsPtr, "Double-", 7);
  4148.         }
  4149.     }
  4150.     for (needMods = patPtr->needMods, modPtr = modArray;
  4151.         needMods != 0; modPtr++) {
  4152.         if (modPtr->mask & needMods) {
  4153.         needMods &= ~modPtr->mask;
  4154.         Tcl_DStringAppend(dsPtr, modPtr->name, -1);
  4155.         Tcl_DStringAppend(dsPtr, "-", 1);
  4156.         }
  4157.     }
  4158.     for (eiPtr = eventArray; eiPtr->name != NULL; eiPtr++) {
  4159.         if (eiPtr->type == patPtr->eventType) {
  4160.         Tcl_DStringAppend(dsPtr, eiPtr->name, -1);
  4161.         if (patPtr->detail.clientData != 0) {
  4162.             Tcl_DStringAppend(dsPtr, "-", 1);
  4163.         }
  4164.         break;
  4165.         }
  4166.     }
  4167.  
  4168.     if (patPtr->detail.clientData != 0) {
  4169.         if ((patPtr->eventType == KeyPress)
  4170.             || (patPtr->eventType == KeyRelease)) {
  4171.         char *string;
  4172.  
  4173.         string = TkKeysymToString(patPtr->detail.keySym);
  4174.         if (string != NULL) {
  4175.             Tcl_DStringAppend(dsPtr, string, -1);
  4176.         }
  4177.         } else {
  4178.         sprintf(buffer, "%d", patPtr->detail.button);
  4179.         Tcl_DStringAppend(dsPtr, buffer, -1);
  4180.         }
  4181.     }
  4182.     Tcl_DStringAppend(dsPtr, ">", 1);
  4183.     }
  4184. }
  4185.  
  4186. /*
  4187.  *----------------------------------------------------------------------
  4188.  *
  4189.  * GetKeySym --
  4190.  *
  4191.  *    Given an X KeyPress or KeyRelease event, map the
  4192.  *    keycode in the event into a KeySym.
  4193.  *
  4194.  * Results:
  4195.  *    The return value is the KeySym corresponding to
  4196.  *    eventPtr, or NoSymbol if no matching Keysym could be
  4197.  *    found.
  4198.  *
  4199.  * Side effects:
  4200.  *    In the first call for a given display, keycode-to-
  4201.  *    KeySym maps get loaded.
  4202.  *
  4203.  *----------------------------------------------------------------------
  4204.  */
  4205.  
  4206. static KeySym
  4207. GetKeySym(dispPtr, eventPtr)
  4208.     TkDisplay *dispPtr;    /* Display in which to
  4209.                      * map keycode. */
  4210.     XEvent *eventPtr;        /* Description of X event. */
  4211. {
  4212.     KeySym sym;
  4213.     int index;
  4214.  
  4215.     /*
  4216.      * Refresh the mapping information if it's stale
  4217.      */
  4218.  
  4219.     if (dispPtr->bindInfoStale) {
  4220.     InitKeymapInfo(dispPtr);
  4221.     }
  4222.  
  4223.     /*
  4224.      * Figure out which of the four slots in the keymap vector to
  4225.      * use for this key.  Refer to Xlib documentation for more info
  4226.      * on how this computation works.
  4227.      */
  4228.  
  4229.     index = 0;
  4230.     if (eventPtr->xkey.state & dispPtr->modeModMask) {
  4231.     index = 2;
  4232.     }
  4233.     if ((eventPtr->xkey.state & ShiftMask)
  4234.         || ((dispPtr->lockUsage != LU_IGNORE)
  4235.         && (eventPtr->xkey.state & LockMask))) {
  4236.     index += 1;
  4237.     }
  4238.     sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode, index);
  4239.  
  4240.     /*
  4241.      * Special handling:  if the key was shifted because of Lock, but
  4242.      * lock is only caps lock, not shift lock, and the shifted keysym
  4243.      * isn't upper-case alphabetic, then switch back to the unshifted
  4244.      * keysym.
  4245.      */
  4246.  
  4247.     if ((index & 1) && !(eventPtr->xkey.state & ShiftMask)
  4248.         && (dispPtr->lockUsage == LU_CAPS)) {
  4249.     if (!(((sym >= XK_A) && (sym <= XK_Z))
  4250.         || ((sym >= XK_Agrave) && (sym <= XK_Odiaeresis))
  4251.         || ((sym >= XK_Ooblique) && (sym <= XK_Thorn)))) {
  4252.         index &= ~1;
  4253.         sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode,
  4254.             index);
  4255.     }
  4256.     }
  4257.  
  4258.     /*
  4259.      * Another bit of special handling:  if this is a shifted key and there
  4260.      * is no keysym defined, then use the keysym for the unshifted key.
  4261.      */
  4262.  
  4263.     if ((index & 1) && (sym == NoSymbol)) {
  4264.     sym = XKeycodeToKeysym(dispPtr->display, eventPtr->xkey.keycode,
  4265.             index & ~1);
  4266.     }
  4267.     return sym;
  4268. }
  4269.  
  4270. /*
  4271.  *--------------------------------------------------------------
  4272.  *
  4273.  * InitKeymapInfo --
  4274.  *
  4275.  *    This procedure is invoked to scan keymap information
  4276.  *    to recompute stuff that's important for binding, such
  4277.  *    as the modifier key (if any) that corresponds to "mode
  4278.  *    switch".
  4279.  *
  4280.  * Results:
  4281.  *    None.
  4282.  *
  4283.  * Side effects:
  4284.  *    Keymap-related information in dispPtr is updated.
  4285.  *
  4286.  *--------------------------------------------------------------
  4287.  */
  4288.  
  4289. static void
  4290. InitKeymapInfo(dispPtr)
  4291.     TkDisplay *dispPtr;        /* Display for which to recompute keymap
  4292.                  * information. */
  4293. {
  4294.     XModifierKeymap *modMapPtr;
  4295.     KeyCode *codePtr;
  4296.     KeySym keysym;
  4297.     int count, i, j, max, arraySize;
  4298. #define KEYCODE_ARRAY_SIZE 20
  4299.  
  4300.     dispPtr->bindInfoStale = 0;
  4301.     modMapPtr = XGetModifierMapping(dispPtr->display);
  4302.  
  4303.     /*
  4304.      * Check the keycodes associated with the Lock modifier.  If
  4305.      * any of them is associated with the XK_Shift_Lock modifier,
  4306.      * then Lock has to be interpreted as Shift Lock, not Caps Lock.
  4307.      */
  4308.  
  4309.     dispPtr->lockUsage = LU_IGNORE;
  4310.     codePtr = modMapPtr->modifiermap + modMapPtr->max_keypermod*LockMapIndex;
  4311.     for (count = modMapPtr->max_keypermod; count > 0; count--, codePtr++) {
  4312.     if (*codePtr == 0) {
  4313.         continue;
  4314.     }
  4315.     keysym = XKeycodeToKeysym(dispPtr->display, *codePtr, 0);
  4316.     if (keysym == XK_Shift_Lock) {
  4317.         dispPtr->lockUsage = LU_SHIFT;
  4318.         break;
  4319.     }
  4320.     if (keysym == XK_Caps_Lock) {
  4321.         dispPtr->lockUsage = LU_CAPS;
  4322.         break;
  4323.     }
  4324.     }
  4325.  
  4326.     /*
  4327.      * Look through the keycodes associated with modifiers to see if
  4328.      * the the "mode switch", "meta", or "alt" keysyms are associated
  4329.      * with any modifiers.  If so, remember their modifier mask bits.
  4330.      */
  4331.  
  4332.     dispPtr->modeModMask = 0;
  4333.     dispPtr->metaModMask = 0;
  4334.     dispPtr->altModMask = 0;
  4335.     codePtr = modMapPtr->modifiermap;
  4336.     max = 8*modMapPtr->max_keypermod;
  4337.     for (i = 0; i < max; i++, codePtr++) {
  4338.     if (*codePtr == 0) {
  4339.         continue;
  4340.     }
  4341.     keysym = XKeycodeToKeysym(dispPtr->display, *codePtr, 0);
  4342.     if (keysym == XK_Mode_switch) {
  4343.         dispPtr->modeModMask |= ShiftMask << (i/modMapPtr->max_keypermod);
  4344.     }
  4345.     if ((keysym == XK_Meta_L) || (keysym == XK_Meta_R)) {
  4346.         dispPtr->metaModMask |= ShiftMask << (i/modMapPtr->max_keypermod);
  4347.     }
  4348.     if ((keysym == XK_Alt_L) || (keysym == XK_Alt_R)) {
  4349.         dispPtr->altModMask |= ShiftMask << (i/modMapPtr->max_keypermod);
  4350.     }
  4351.     }
  4352.  
  4353.     /*
  4354.      * Create an array of the keycodes for all modifier keys.
  4355.      */
  4356.  
  4357.     if (dispPtr->modKeyCodes != NULL) {
  4358.     ckfree((char *) dispPtr->modKeyCodes);
  4359.     }
  4360.     dispPtr->numModKeyCodes = 0;
  4361.     arraySize = KEYCODE_ARRAY_SIZE;
  4362.     dispPtr->modKeyCodes = (KeyCode *) ckalloc((unsigned)
  4363.         (KEYCODE_ARRAY_SIZE * sizeof(KeyCode)));
  4364.     for (i = 0, codePtr = modMapPtr->modifiermap; i < max; i++, codePtr++) {
  4365.     if (*codePtr == 0) {
  4366.         continue;
  4367.     }
  4368.  
  4369.     /*
  4370.      * Make sure that the keycode isn't already in the array.
  4371.      */
  4372.  
  4373.     for (j = 0; j < dispPtr->numModKeyCodes; j++) {
  4374.         if (dispPtr->modKeyCodes[j] == *codePtr) {
  4375.         goto nextModCode;
  4376.         }
  4377.     }
  4378.     if (dispPtr->numModKeyCodes >= arraySize) {
  4379.         KeyCode *new;
  4380.  
  4381.         /*
  4382.          * Ran out of space in the array;  grow it.
  4383.          */
  4384.  
  4385.         arraySize *= 2;
  4386.         new = (KeyCode *) ckalloc((unsigned)
  4387.             (arraySize * sizeof(KeyCode)));
  4388.         memcpy((VOID *) new, (VOID *) dispPtr->modKeyCodes,
  4389.             (dispPtr->numModKeyCodes * sizeof(KeyCode)));
  4390.         ckfree((char *) dispPtr->modKeyCodes);
  4391.         dispPtr->modKeyCodes = new;
  4392.     }
  4393.     dispPtr->modKeyCodes[dispPtr->numModKeyCodes] = *codePtr;
  4394.     dispPtr->numModKeyCodes++;
  4395.     nextModCode: continue;
  4396.     }
  4397.     XFreeModifiermap(modMapPtr);
  4398. }
  4399.  
  4400. /*
  4401.  *---------------------------------------------------------------------------
  4402.  *
  4403.  * EvalTclBinding --
  4404.  *
  4405.  *    The procedure that is invoked by Tk_BindEvent when a Tcl binding
  4406.  *    is fired.  
  4407.  *
  4408.  * Results:
  4409.  *    A standard Tcl result code, the result of globally evaluating the
  4410.  *    percent-substitued binding string.
  4411.  *
  4412.  * Side effects:
  4413.  *    Normal side effects due to eval.
  4414.  *
  4415.  *---------------------------------------------------------------------------
  4416.  */
  4417.  
  4418. static void
  4419. FreeTclBinding(clientData)
  4420.     ClientData clientData;
  4421. {
  4422.     ckfree((char *) clientData);
  4423. }
  4424.  
  4425. /*
  4426.  *----------------------------------------------------------------------
  4427.  *
  4428.  * TkStringToKeysym --
  4429.  *
  4430.  *    This procedure finds the keysym associated with a given keysym
  4431.  *    name.
  4432.  *
  4433.  * Results:
  4434.  *    The return value is the keysym that corresponds to name, or
  4435.  *    NoSymbol if there is no such keysym.
  4436.  *
  4437.  * Side effects:
  4438.  *    None.
  4439.  *
  4440.  *----------------------------------------------------------------------
  4441.  */
  4442.  
  4443. KeySym
  4444. TkStringToKeysym(name)
  4445.     char *name;            /* Name of a keysym. */
  4446. {
  4447. #ifdef REDO_KEYSYM_LOOKUP
  4448.     Tcl_HashEntry *hPtr;
  4449.     KeySym keysym;
  4450.  
  4451.     hPtr = Tcl_FindHashEntry(&keySymTable, name);
  4452.     if (hPtr != NULL) {
  4453.     return (KeySym) Tcl_GetHashValue(hPtr);
  4454.     }
  4455.     if (strlen(name) == 1) {
  4456.     keysym = (KeySym) (unsigned char) name[0];
  4457.     if (TkKeysymToString(keysym) != NULL) {
  4458.         return keysym;
  4459.     }
  4460.     }
  4461. #endif /* REDO_KEYSYM_LOOKUP */
  4462.     return XStringToKeysym(name);
  4463. }
  4464.  
  4465. /*
  4466.  *----------------------------------------------------------------------
  4467.  *
  4468.  * TkKeysymToString --
  4469.  *
  4470.  *    This procedure finds the keysym name associated with a given
  4471.  *    keysym.
  4472.  *
  4473.  * Results:
  4474.  *    The return value is a pointer to a static string containing
  4475.  *    the name of the given keysym, or NULL if there is no known name.
  4476.  *
  4477.  * Side effects:
  4478.  *    None.
  4479.  *
  4480.  *----------------------------------------------------------------------
  4481.  */
  4482.  
  4483. char *
  4484. TkKeysymToString(keysym)
  4485.     KeySym keysym;
  4486. {
  4487. #ifdef REDO_KEYSYM_LOOKUP
  4488.     Tcl_HashEntry *hPtr;
  4489.  
  4490.     hPtr = Tcl_FindHashEntry(&nameTable, (char *)keysym);
  4491.     if (hPtr != NULL) {
  4492.     return (char *) Tcl_GetHashValue(hPtr);
  4493.     }
  4494. #endif /* REDO_KEYSYM_LOOKUP */
  4495.     return XKeysymToString(keysym);
  4496. }
  4497.  
  4498. /*
  4499.  *----------------------------------------------------------------------
  4500.  *
  4501.  * TkCopyAndGlobalEval --
  4502.  *
  4503.  *    This procedure makes a copy of a script then calls Tcl_GlobalEval
  4504.  *    to evaluate it.  It's used in situations where the execution of
  4505.  *    a command may cause the original command string to be reallocated.
  4506.  *
  4507.  * Results:
  4508.  *    Returns the result of evaluating script, including both a standard
  4509.  *    Tcl completion code and a string in interp->result.
  4510.  *
  4511.  * Side effects:
  4512.  *    None.
  4513.  *
  4514.  *----------------------------------------------------------------------
  4515.  */
  4516.  
  4517. int
  4518. TkCopyAndGlobalEval(interp, script)
  4519.     Tcl_Interp *interp;            /* Interpreter in which to evaluate
  4520.                      * script. */
  4521.     char *script;            /* Script to evaluate. */
  4522. {
  4523.     Tcl_DString buffer;
  4524.     int code;
  4525.  
  4526.     Tcl_DStringInit(&buffer);
  4527.     Tcl_DStringAppend(&buffer, script, -1);
  4528.     code = Tcl_GlobalEval(interp, Tcl_DStringValue(&buffer));
  4529.     Tcl_DStringFree(&buffer);
  4530.     return code;
  4531. }
  4532.  
  4533.  
  4534.